robotium腳本封裝為APK,實現脫離手機數據線,使用按鈕點擊控制用例運行的小功能


最近一直在完成一些robotium的小功能,用來更方便的完成一些小功能的測試,或者可以說用來娛樂吧,幸得群內大神思路指點,就此引申,終於把這個功能得以實現

---------------將robotium腳本封裝為APK,使用按鈕控制用例運行覆蓋程度,測試結果以簡單的xml文件輸入到手機SD卡目錄下----------------------

廢話不多說,轉正題:

一、首先明確一點,這篇文章,是在你所編寫的robotium腳本運行無異常的前提下實施

二、闡明思路:

   1.我們需要一個運行良好的,邏輯正常的robotium工程

   2.我們需要一個可以將腳本用例運行結果保存至本地的方法

   3.我們需要一個activity,一個按鈕,以及一個按鈕點擊事件去運行我們的robotium腳本

 

三、先介紹腳本用例運行結果的功能,我們都知道robotium用例的運行是依賴junit的Instrumentation的,所以,關於結果的輸出,需要我們重寫InstrumentationTestRunner類

 1 package baih;  2  3 import java.io.File;  4 import java.io.FileWriter;  5 import java.io.IOException;  6 import java.io.Writer;  7  8 import org.xmlpull.v1.XmlPullParserFactory;  9 import org.xmlpull.v1.XmlSerializer;  10  11 import android.content.Context;  12 import android.os.Bundle;  13 import android.os.Environment;  14  15 /**  16  * This test runner creates a TEST-all.xml in the files directory of the application under test. The output is compatible with that of the junitreport ant task, the format  17  * that is understood by Hudson. Currently this implementation does not implement the all aspects of the junitreport format, but enough for Hudson to parse the test results.  18 */  19 public class InstrumentationTestRunner extends android.test.InstrumentationTestRunner {  20 private Writer mWriter;  21 private XmlSerializer mTestSuiteSerializer;  22 private long mTestStarted;  23 private static final String JUNIT_XML_FILE = "TEST-all.xml";  24  25  26  @Override  27 public void onStart() {  28 try{  29 File fileRobo = new File(getTestResultDir(getTargetContext()));  30 if(!fileRobo.exists()){  31  fileRobo.mkdir();  32  }  33 if(isSDCardAvaliable()){  34 File resultFile = new File(getTestResultDir(getTargetContext()),JUNIT_XML_FILE);  35 startJUnitOutput(new FileWriter(resultFile));  36 }else{  37 startJUnitOutput(new FileWriter(new File(getTargetContext().getFilesDir(), JUNIT_XML_FILE)));  38  }  39  }  40 catch(IOException e){  41 throw new RuntimeException(e);  42  }  43 super.onStart();  44  }  45  46 void startJUnitOutput(Writer writer) {  47 try {  48 mWriter = writer;  49 mTestSuiteSerializer = newSerializer(mWriter);  50 mTestSuiteSerializer.startDocument(null, null);  51 mTestSuiteSerializer.startTag(null, "testsuites");  52 mTestSuiteSerializer.startTag(null, "testsuite");  53 } catch (Exception e) {  54 throw new RuntimeException(e);  55  }  56  }  57  58 /**  59  * 判斷SD卡是否存在  60  * @return  61 */  62 private boolean isSDCardAvaliable(){  63 return Environment.getExternalStorageState()  64  .equals(Environment.MEDIA_MOUNTED);  65  }  66  67 /**  68  * 獲取測試結果報告文件所在的路徑  69  * @param context 被測工程的context  70  * @return 返回測試結果報告文件所在的路徑  71 */  72 private String getTestResultDir(Context context){  73 String packageName = "/" + "robotium";  74 String filepath = context.getCacheDir().getPath() + packageName;  75  76 if(android.os.Build.VERSION.SDK_INT < 8){  77 if(isSDCardAvaliable()){ 78 filepath = Environment.getExternalStorageDirectory().getAbsolutePath()+ packageName; 79 } 80 }else{ 81 if(isSDCardAvaliable()){ 82 filepath = Environment.getExternalStorageDirectory().getAbsolutePath()+ packageName; 83 } 84 } 85 return filepath; 86 } 87 88 private XmlSerializer newSerializer(Writer writer) { 89 try { 90 XmlPullParserFactory pf = XmlPullParserFactory.newInstance(); 91 XmlSerializer serializer = pf.newSerializer(); 92 serializer.setOutput(writer); 93 return serializer; 94 } catch (Exception e) { 95 throw new RuntimeException(e); 96 } 97 } 98 99 @Override 100 public void sendStatus(int resultCode, Bundle results) { 101 super.sendStatus(resultCode, results); 102 switch (resultCode) { 103 case REPORT_VALUE_RESULT_ERROR: 104 case REPORT_VALUE_RESULT_FAILURE: 105 case REPORT_VALUE_RESULT_OK: 106 try { 107 recordTestResult(resultCode, results); 108 } catch (IOException e) { 109 throw new RuntimeException(e); 110 } 111 break; 112 case REPORT_VALUE_RESULT_START: 113 recordTestStart(results); 114 default: 115 break; 116 } 117 } 118 119 void recordTestStart(Bundle results) { 120 mTestStarted = System.currentTimeMillis(); 121 } 122 123 void recordTestResult(int resultCode, Bundle results) throws IOException { 124 float time = (System.currentTimeMillis() - mTestStarted) / 1000.0f; 125 String className = results.getString(REPORT_KEY_NAME_CLASS); 126 String testMethod = results.getString(REPORT_KEY_NAME_TEST); 127 String stack = results.getString(REPORT_KEY_STACK); 128 int current = results.getInt(REPORT_KEY_NUM_CURRENT); 129 int total = results.getInt(REPORT_KEY_NUM_TOTAL); 130 131 mTestSuiteSerializer.startTag(null, "testcase"); 132 mTestSuiteSerializer.attribute(null, "classname", className); 133 mTestSuiteSerializer.attribute(null, "name", testMethod); 134 135 if (resultCode != REPORT_VALUE_RESULT_OK) { 136 mTestSuiteSerializer.startTag(null, "failure"); 137 if (stack != null) { 138 String reason = stack.substring(0, stack.indexOf('\n')); 139 String message = ""; 140 int index = reason.indexOf(':'); 141 if (index > -1) { 142 message = reason.substring(index+1); 143 reason = reason.substring(0, index); 144 } 145 mTestSuiteSerializer.attribute(null, "message", message); 146 mTestSuiteSerializer.attribute(null, "type", reason); 147 mTestSuiteSerializer.text(stack); 148 } 149 mTestSuiteSerializer.endTag(null, "failure"); 150 } else { 151 mTestSuiteSerializer.attribute(null, "time", String.format("%.3f", time)); 152 } 153 mTestSuiteSerializer.endTag(null, "testcase"); 154 if (current == total) { 155 mTestSuiteSerializer.startTag(null, "system-out"); 156 mTestSuiteSerializer.endTag(null, "system-out"); 157 mTestSuiteSerializer.startTag(null, "system-err"); 158 mTestSuiteSerializer.endTag(null, "system-err"); 159 mTestSuiteSerializer.endTag(null, "testsuite"); 160 mTestSuiteSerializer.flush(); 161 } 162 } 163 164 @Override 165 public void finish(int resultCode, Bundle results) { 166 endTestSuites(); 167 super.finish(resultCode, results); 168 } 169 170 void endTestSuites() { 171 try { 172 mTestSuiteSerializer.endTag(null, "testsuites"); 173 mTestSuiteSerializer.endDocument(); 174 mTestSuiteSerializer.flush(); 175 mWriter.flush(); 176 mWriter.close(); 177 } catch (IOException e) { 178 throw new RuntimeException(e); 179 } 180 } 181 }

重 寫InstrumentationTestRunner類后,需要在我們的測試工程下設置run Configurations,在你的測試工程上右鍵-移至Run As-run Configurations-android JUnit Test,更改你工程運行的Instrumentation runner,如果不更改,默認是android.test.InstrumentationTestRunner

紅色框體圈住的位置需要更改為你重寫的InstrumentationTestrunner路徑

完成以上步驟后,你再運行robotium腳本,就會在你的手機根目錄下名為robotium的文件夾中生成一個名為TEST-ALL.xml的文件,用來記錄你的腳本運行結果,結構如下:

 

運行正常的結果,會顯示運行時長,運行錯誤的用例,會打印錯誤信息,至於錯誤信息中的中文提示,是我用例中自己加的斷言,各位可以自行斟酌

 

完成這一步,就可以實施我們的封裝APK計划了。封裝APK,第一,我們需要一個activity

 

界面比較挫,將就看。關於activity的創建和聲明,請各位自行百度

activity中可以放置一個或多個按鈕,由於robotium可以使用命令行進行啟動運行,所以我們可以使用不同按鈕的onclick事件來執行不同的腳本

 

如上:附上各種命令行啟動命令:

運行所有測試用例:

adb shell am instrument -w packagename/InstrumentationTestRunnername

運行單個測試類或某個TestSuite
adb shell am instrument -e class packagename.testclassname -w packagename/InstrumentationTestRunnername

運行某個測試類里面的某個測試方法
adb shell am instrument -e class package名.測試類名#方法名 -w 工程名.package名/InstrumentationTestRunner class名

注意:請在編寫代碼前,在CMD中測試你所使用的命令是否可以正常運行

完成以上步驟后,我們可以將整個測試工程打包為APK文件,記得簽名需要簽debug簽名,然后......安裝到你的手機上,拔掉該死的數據線,點擊按鈕,開始自動虐你的手機吧:)

號外!號外!

由於手頭上一直沒有android level 17及以上版本的手機,有一個shell命令啟動腳本的BUG,發生在SDK level 17及以上

API>=17中加入了INTERACT_ACROSS_USERS_FULL,目的在於允許不同用戶的應用之間可以產生交互,了安全,因此在交互時會校驗userSerialNumber,,發現用戶標識不匹配,導致權限校驗失敗,就會產生startInstrumentation asks to run as user -2 but is calling from user 0; this requires android.permission.INTERACT_ACROSS_USERS_FULL的報錯,導致腳本無法調用

群里嘗試,發現在17及以上版本,命令中需要加入--user 0參數

adb shell am instrument --user 0 -w packagename/InstrumentationTestRunnername

可以在調用時使用Build.VERSION.SDK_INT<17來對當前版本做判斷選擇合適的命令行啟動方式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM