最近一直在完成一些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签名,然后......安装到你的手机上,拔掉该死的数据线,点击按钮,开始自动虐你的手机吧:)