1. AndroidJUnitRunner介绍
AndroidJUnitRunner类是一个JUnit测试运行器,允许运行JUnit 3或JUnit 4测试类在Android设备上,包括那些使用Espresso和UI Automator框架。
2. AndroidJUnitRunner过滤器及参数
-e testFile <filePath> : 运行文件中指定的用例。
-e package <packageName>: 运行这个包中的所有用例。
-e size [small |medium| large]: 运行注解为SmallTest/MediumTest/LargeTest的用例。
-e annotation <annotation>: 运行指定注解的用例。
-e notAnnotation <annotation>: 运行不包含指定注解的用例。
-e numShards <num>: 将用例分割成不同的切片。
-e shardIndex <id>: 运行指定切片的用例。
-e debug true : 可以在代码中设置断点,通过此参数进行调试测试脚本。
-e log true: 只在日志模式下运行,将加载和遍历所有的测试类和方法,但会绕过实际的测试执行,用于快速获取通过instrumentation命令执行测试的信息。
-e coverage true: 生成代码覆盖率文件,这需要是一个集成了EMMA或JaCoCo的构建。 默认情况下,代码覆盖率结果文件将被保存在/data/data//files/coverage.ec文件,另外也可以自定义覆盖率结果文件的路径,e.g -e coverageFile /sdcard/myFile.ec。
-e listener <runlistener>: 指定一个或者多个运行监听器观察测试运行,可用于定制测试报告,e.g. -e listener com.foo.Listener,com.foo.Listener2.
-e timeout_msec <int> :设置超时时间,将被应用到每一个测试,e.g.-e timeout_msec 5000.
-e disableAnalytics true: 禁用谷歌分析, 主要是用于谷歌进行收集数据.
3. 帮助示例
3.1 运行所有的用例: adb shell am instrument -w com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.2 运行一个类中的所有用例: adb shell am instrument -w -e class com.android.foo.FooTest com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.3 运行单个测试用例:
db shell am instrument -w -e class com.android.foo.FooTest#testFoo com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.4 运行多个类的所有用例:
adb shell am instrument -w -e class com.android.foo.FooTest,com.android.foo.TooTest com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.5 运行所有测试用例除了指定的类:
adb shell am instrument -w -e notClass com.android.foo.FooTest com.android.foo/android.support.test.runner.AndroidJUnitRunner3.6 运行所有测试除了指定的用例: adb shell am instrument -w -e notClass com.android.foo.FooTest#testFoo com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.7 运行文件中的所列的用例:
adb shell am instrument -w -e testFile /sdcard/tmp/testFile.txt com.android.foo/com.android.test.runner.AndroidJUnitRunner
文件制定的格式为:com.android.foo.FooClaseName#testMethodName
3.8 运行JAVA包中的所有测试用例: adb shell am instrument -w -e package com.android.foo.bar com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.9 运行指定测试切片的用例:adb shell am instrument -w -e numShards 4 -e shardIndex 1 com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.10 运行指定注解的测试用例:adb shell am instrument -w -e annotation com.android.foo.MyAnnotation com.android.foo/android.support.test.runner.AndroidJUnitRunner。如果使用多个选项,则运行的用例为两者的交集,比如:“-e size large -e annotation com.android.foo.MyAnnotation”,将只运行同时含LargeTest和MyAnnotation注解的用例。
3.11 运行没有指定注解的用例:adb shell am instrument -w -e notAnnotation com.android.foo.MyAnnotation com.android.foo/android.support.test.runner.AndroidJUnitRunner,指定多个注解,用“,”隔开,e.g. adb shell am instrument -w -e notAnnotation com.android.foo.MyAnnotation,com.android.foo.AnotherAnnotation com.android.foo/android.support.test.runner.AndroidJUnitRunner
3.12 以上所有参数也可以通过<meta-data>标签配置在AndroidManifest文件,比如 <meta-data android:name="listener" android:value="com.foo.Listener"/>,通过shell命令传入的参数将覆盖AndroidManifest文件中配置的参数。
4. 工程实践
4.1 通过测试切片,运行测试:
测试用例:
@RunWith(AndroidJUnit4.class) public class TestClass01 { public UiDevice mDevice; public Instrumentation instrumentation; @Before public void setUp(){ instrumentation = InstrumentationRegistry.getInstrumentation(); mDevice = UiDevice.getInstance(instrumentation); } @Test public void testCase01() throws IOException { //test code } @Test public void testCase02(){ //test code } @Test public void testCase03(){ //test code } @Test public void testCase04(){ //test code } }
执行测试:
am instrument -w -r -e numShards 4 -e shardIndex 1 com.test.tommyxie.hellouiautomator.test/android.support.test.runner.AndroidJUnitRunner
遇到的坑:
1. 切片数需要能被用例数整除,否则切片不成功,如下:
am instrument -w -r -e numShards 3 -e shardIndex 1 com.test.tommyxie.hellouiautomator.test/android.support.test.runner.AndroidJUnitRunner
2. 切片的索引为从0开始算起,比如切片为4份,指定运行的索引为4时,将会执行所有用例:
am instrument -w -r -e numShards 4 -e shardIndex 4 com.test.tommyxie.hellouiautomator.test/android.support.test.runner.AndroidJUnitRunner
rds 4 -e shardIndex 4 com.test.tommyxie.hellouiautomator.test/android.support* INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= com.test.tommyxie.hellouiautomator.TestClass01: INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase01 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase01 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase02 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=2 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase02 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=2 INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase03 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=3 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase03 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=3 INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase04 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=4 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testCase04 INSTRUMENTATION_STATUS: class=com.test.tommyxie.hellouiautomator.TestClass01 INSTRUMENTATION_STATUS: current=4 INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_RESULT: stream= Time: 0.131 OK (4 tests)
4.2 通过自定义注解,运行测试:
参照既有的注解的格式e.g.LargeTest注解,新建一个定义注解:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface CustomAnnotation { }测试用例引用自定义注解:
@RunWith(AndroidJUnit4.class) public class TestClass01 { public UiDevice mDevice; public Instrumentation instrumentation; @Before public void setUp(){ instrumentation = InstrumentationRegistry.getInstrumentation(); mDevice = UiDevice.getInstance(instrumentation); } @Test public void testCase01() throws IOException { //test code } @Test @CustomAnnotation public void testCase02(){ //test code } @Test public void testCase03(){ //test code } @Test public void testCase04(){ //test code } }
am instrument -w -r -e annotation com.test.tommyxie.hellouiautomator.CustomAnnotation com.test.tommyxie.hellouiautomator.test/android.support.test.runner.AndroidJUnitRunner
4.3 通过AndroidManifest文件配置参数,运行测试,e.g.指定注解运行测试:
Manifest 文件配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.test.tommyxie.hellouiautomator"> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:theme="@style/AppTheme"> <instrumentation android:name="com.android.support.test.runner.AndroidJUnitRunner" android:targetPackage="com.test.tommyxie.hellouiautomator"> <meta-data android:name="annotation" android:value="com.test.tommyxie.hellouiautomator.CustomAnnotation" /> </instrumentation> </application> </manifest>