android 单元测试(1)无依赖测试
单元测试不适用于测试复杂的界面交互事件。后者应改用界面测试框架。
1.官方文档
https://developer.android.google.cn/training/testing/unit-testing?hl=zh-cn
https://github.com/android/testing-samples/tree/master/unit/BasicUnitAndroidTest
https://junit.org/junit4/index.html
https://junit.org/junit4/javadoc/latest/overview-summary.html
https://github.com/junit-team/junit4/wiki/Getting-started junit使用教程
https://developer.android.google.cn/training/testing/set-up-project
https://developer.android.google.cn/training/testing/fundamentals#complete-testing-tasks
2.使用lint
使用lint扫出错误、警告,然后解决它们,可以先排出很多问题。
3.添加依赖项
顶级 build.gradle
文件中添加 junit:junit :
1 dependencies { 2 3 ... 4 testImplementation 'junit:junit:4.+' 5 androidTestImplementation 'androidx.test.ext:junit:1.1.2' 6 ... 7 }
4.编写被测试类、测试代码
4.1 使用junit api测试
被测试的类:
1 class Test1 { 2 3 fun gt(a : Int,b : Int) = a > b 4 fun lt(a : Int,b : Int) = a < b 5 6 }
测试代码:
测试的常用的语句见 junit常用语句
android 目前使用 Java 单元测试框架 JUnit . 简单使用@Test 注解 可添加测试。见 常用注解
如果是类用@Test,要求是可运行的类。
单元测试应尽量囊括与单元的所有可能的互动,包括标准互动、无效输入以及资源不可用等情况。
1 class ExampleUnitTest { 2 @Test 3 fun test1_gt(){ 4 val x = 1 5 val y = 100 6 val test = Test1() 7 8 assertTrue("failure - x greater than y",test.gt(y,x)) 9 assertFalse("failure - x not greater than y",test.lt(x,y)) 10 } 11 }
测试代码的位置
其中 :
- androidTest 目录 包括集成测试 以及仅靠 JVM 无法完成应用功能验证的其他测试。
- test 目录应包含在本地计算机上运行的测试,如单元测试。
4.2 使用 androidX test api测试
官方文档:
https://developer.android.google.cn/training/testing/set-up-project
https://developer.android.google.cn/training/testing/fundamentals#complete-testing-tasks
5.调试或者运行
1.测试类或者函数
调试或者运行后,全绿色表示全部通过。
2.运行目录中的所有测试
右键目录 --> Run test
6.生成报告
报告目录默认在项目根目录下,
7.常用的junit测试语句
https://junit.org/junit4/index.html
assert系列 |
void assertTrue(String message, boolean condition) |
断言条件为真,若非:抛出AssertionError异常(内容为message) |
void assertTrue(boolean condition) |
断言条件为真,若非,抛出AssertionError异常 |
|
void assertFalse(String message, boolean condition) |
断言条件为假,若非:抛出AssertionError异常(内容为message) |
|
void assertFalse(boolean condition) |
断言条件为假,若非:抛出AssertionError异常 |
|
void assertEquals(String message, Object expected, Object actual) |
断言expected与actual相等,若非:抛出AssertionError异常(message) 当两个都是空时,认为相等。 |
|
void assertEquals(Object expected, Object actual) |
断言expected与actual相等,若非:抛出AssertionError异常 当两个都是空时,认为相等。 |
|
void assertNotEquals(String message, Object unexpected, Object actual) |
断言不等,若非:抛出AssertionError异常(message) 当两个都是空时,认为相等。 |
|
void assertNotEquals(Object unexpected, Object actual) |
断言不等,若非:抛出AssertionError异常 当两个都是空时,认为相等。 |
|
void assertNotEquals(String message, long unexpected, long actual) |
断言不等,若非:抛出AssertionError异常(message) 两个都是long类型 |
|
void assertNotEquals(long unexpected, long actual) |
断言unexpected与actual不等,若非:抛出AssertionError异常 两个都是long类型 |
|
... | ... | |
assert数组 |
void assertArrayEquals(boolean[] expecteds, boolean[] actuals) |
断言相等,若非:抛出AssertionError异常 当两个都是空时,认为相等。 |
void assertArrayEquals(String message, boolean[] expecteds, boolean[] actuals) |
断言相等,若非:抛出AssertionError异常(message) 当两个都是空时,认为相等。 |
|
类似的还有Object[]、byte[]、char[]、short[]、int[]、long[]、double[]、float[] |
||
fail系列 |
void fail(String message) |
Fails a test with the given message. |
void fail(String message) |
Fails a test with the given message. |
|
还有一些私有的,不能直接调用的:failEquals、failNotEquals、failNotNull、failNotSame、failSame |
8.常用注解
@Test
标注测试类或者测试方法,如
1 @Test 2 fun addition_isCorrect() { 3 assertEquals(4, 2 + 2) 4 }
@Test(timeout=1000)
设置测试超时时间,到时会自动失败,单位是毫秒。
1 @Test(timeout = 3000) 2 fun test_timeout(){ 3 var time = 1 4 while (time++ < 5){ 5 println("time = $time") 6 Thread.sleep(1000 * 1) 7 } 8 println("test_timeout finished") 9 }
结果:
@Ignore(string)
忽略一个测试,无法运行或者调试,用在@Test之前,如
1 @Ignore("Test is ignored as a demonstration") 2 @Test 3 fun test_ignore(){ 4 println("test_ignore") 5 }
@RunWith
它们只用来注解类,不注解函数。
JUnit用例都是在Runner(运行器)来执行的,用runwith指定运行器,默认的是BlockJUnit4ClassRunner,还有常用的Suite、Categories等,如:
1 @RunWith(BlockJUnit4ClassRunner::class) 2 class TestB{ 3 @Test 4 fun testB_f1(){ 5 println("testB . f1") 6 } 7 } 8 9 @RunWith(Suite::class) 10 class TestC{ 11 @Test 12 fun test_runwith(){ 13 println("TestC . test_runwith") 14 } 15 }
也可以自己定义Runner。
@Suite.SuiteClasses
注解类,与@RunWith(Suite.class)一直使用,指定要测试的类,类中的未标注ignore的用例都被执行。
1 class Suite1{ 2 @Test 3 fun f1(){ 4 println("Suite1 . f1") 5 } 6 @Test 7 fun f2(){ 8 println("Suite1 . f2") 9 } 10 @Ignore("todo") 11 @Test 12 fun f3(){ 13 println("Suite1 . ignore f3") 14 } 15 } 16 17 class Suite2{ 18 @Test 19 fun f1(){ 20 println("Suite2 . f1") 21 } 22 @Test 23 fun f2(){ 24 println("Suite2 . f2") 25 } 26 @Test 27 fun f3(){ 28 println("Suite2 . f3") 29 } 30 } 31 32 @RunWith(Suite::class) 33 @Suite.SuiteClasses(Suite1::class,Suite2::class) 34 class TestC{ 35 @Test 36 fun test_runwith(){ 37 println("TestC . test_runwith") 38 } 39 }
其中Suite1.f3会被忽略,结果如下:
@Category 与 @IncludeCategory、@ExcludeCategory
给测试分类
1 //Category 2 interface Category1 3 interface Category2 4 5 class A{ 6 @Test 7 fun a() { 8 println("A.a()") 9 } 10 @Category(Category2::class) //Category2 11 @Test 12 fun b() { 13 println("A.b()") 14 } 15 } 16 17 @Category(Category1::class,Category2::class) //Category1,Category2 18 class B{ 19 @Test 20 fun c() { 21 println("B.c()") 22 } 23 } 24 25 @Test 26 @Category(Category1::class,Category2::class) 27 fun test_Category(){ 28 29 } 30 31 @RunWith(Categories::class) 32 @Categories.IncludeCategory(Category1::class) 33 @Suite.SuiteClasses(A::class,A::class) 34 class CategoryInclude{ 35 @Test 36 fun main(){ 37 println("CategoryInclude") 38 } 39 } 40 41 @RunWith(Categories::class) 42 @Categories.ExcludeCategory(Category2::class) 43 @Suite.SuiteClasses(A::class,B::class) 44 class CategoryExclude{ 45 @Test 46 fun main(){ 47 println("CategoryExclude") 48 } 49 }
- 第2、3行定义了两个分类Category1与Category2
- A.b()属于Category2
- B属于Category1和Category2
- 第34行类CategoryInclude测试结果为 B.c()
- 第44行类CategoryExclude测试结果为 A.a()
其它
如 @BeforeClass、@Before、@After、@AfterClass 等,义如其名。