單元測試不適用於測試復雜的界面交互事件。后者應改用界面測試框架。
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 等,義如其名。