Android 單元測試之JUnit
在記錄單元測試有關的筆記前,先談談為什麼寫單元測試
看完這篇文章,相比對單元測試有了一定了解。那麼單元測試如何開始呢
一.建立測試類
在任意需要測試的類(或者方法)下面按下Ctrl+Shift+T(這是預設熱鍵)如下圖**
首次建立一個新的測試類,然後會彈出提示介面:
如果你已經建立過,則會提示對應的測試類讓你跳轉過去,同樣測試類也可以利用這個方法跳轉到被測試類。
建立一個測試類,並編寫測試程式碼,如下圖
二.分析執行結果
執行成功時
執行失敗時
三.批量測試和生產報告
假設你只有一個類或者有幾個類需要測試的話,那可以直接使用上文說的方法來測試,但是假設你有很多的類和方法需要測試的話上面的操作就顯得是十分笨拙。Android Studio的Gradle外掛為我們生成了三個任務:
- testDebugUnitTest
- testReleaseUnitTest
- test
其中前兩個任務是分別執行為Debug和Release模式下的所有單元測試,第三個任務就是執行前面兩個任務。
- 方法:可以直接在面板中選擇Task執行(使用本地的Gradle)
等待執行完成就可以看到build/reports/tests/目錄下對應的Html報告:
使用瀏覽器開啟可以看到詳細測試報告:
四.Assert類中主要方法
方法名 | 含義 |
---|---|
assertEquals | 斷言傳入的預期值與實際值是相等的 |
assertNotEquals | 斷言傳入的預期值與實際值是不相等的 |
assertArrayEquals | 斷言傳入的預期陣列與實際陣列是相等的 |
assertNull | 斷言傳入的物件是為空 |
assertNotNull | 斷言傳入的物件是不為空 |
assertTrue | 斷言條件為真 |
assertFalse | 斷言條件為假 |
assertSame | 斷言兩個物件引用同一個物件,相當於“==” |
assertNotSame | 斷言兩個物件引用不同的物件,相當於“!=” |
assertThat | 斷言實際值是否滿足指定的條件 |
注意:上面的每一個方法,都有對應的過載方法,可以在前面加一個String型別的引數,表示如果斷言失敗時的提示。
assertThat用法
上面我們所用到的一些基本的斷言,如果我們沒有設定失敗時的輸出資訊,那麼在斷言失敗時只會丟擲AssertionError,無法知道到底是哪一部分出錯。而assertThat就幫我們解決了這一點。它的可讀性更好。
assertThat(T actual, Matcher<? super T> matcher);
assertThat(String reason, T actual, Matcher<? super T> matcher);
其中reason為斷言失敗時的輸出資訊,actual為斷言的值,matcher為斷言的匹配器。
下圖為使用assertThat測試失敗時所顯示的具體錯誤資訊。可以看到錯誤資訊很詳細!
常用的匹配器整理
匹配器 | 說明 | 例子 |
---|---|---|
is | 斷言引數等於後面給出的匹配表示式 | assertThat(5, is (5)); |
not | 斷言引數不等於後面給出的匹配表示式 | assertThat(5, not(6)); |
equalTo | 斷言引數相等 | assertThat(30, equalTo(30)); |
equalToIgnoringCase | 斷言字串相等忽略大小寫 | assertThat(“Ab”, equalToIgnoringCase(“ab”)); |
containsString | 斷言字串包含某字串 | assertThat(“abc”, containsString(“bc”)); |
startsWith | 斷言字串以某字串開始 | assertThat(“abc”, startsWith(“a”)); |
endsWith | 斷言字串以某字串結束 | assertThat(“abc”, endsWith(“c”)); |
nullValue | 斷言引數的值為null | assertThat(null, nullValue()); |
notNullValue | 斷言引數的值不為null | assertThat(“abc”, notNullValue()); |
greaterThan | 斷言引數大於 | assertThat(4, greaterThan(3)); |
lessThan | 斷言引數小於 | assertThat(4, lessThan(6)); |
greaterThanOrEqualTo | 斷言引數大於等於 | assertThat(4, greaterThanOrEqualTo(3)); |
lessThanOrEqualTo | 斷言引數小於等於 | assertThat(4, lessThanOrEqualTo(6)); |
closeTo | 斷言浮點型數在某一範圍內 | assertThat(4.0, closeTo(2.6, 4.3)); |
allOf | 斷言符合所有條件,相當於&& | assertThat(4,allOf(greaterThan(3), lessThan(6))); |
anyOf | 斷言符合某一條件,相當於或 | assertThat(4,anyOf(greaterThan(9), lessThan(6))); |
hasKey | 斷言Map集合含有此鍵 | assertThat(map, hasKey(“key”)); |
hasValue | 斷言Map集合含有此值 | assertThat(map, hasValue(value)); |
hasItem | 斷言迭代物件含有此元素 | assertThat(list, hasItem(element)); |
自定義匹配器
只需要繼承BaseMatcher抽象類,實現matches與describeTo方法。程式碼如下:
public class IsMobilePhoneMatcher extends BaseMatcher<String> {
/**
* 進行斷言判定,返回true則斷言成功,否則斷言失敗
*/
@Override
public boolean matches(Object item) {
if (item == null) {
return false;
}
Pattern pattern = Pattern.compile("(1|861)(3|5|7|8)\\d{9}$*");
Matcher matcher = pattern.matcher((String) item);
return matcher.find();
}
/**
* 給預期(Expected)斷言成功的物件增加描述
*/
@Override
public void describeTo(Description description) {
description.appendText("預計此字串是手機號碼!");
}
/**
* 給斷言失敗的物件增加描述
*/
@Override
public void describeMismatch(Object item, Description description) {
description.appendText(item.toString() + "不是手機號碼!");
}
}
@Test
public void testPhone(){
Assert.assertThat("19900003333",new IsMobilePhoneMatcher());
}
執行結果如下圖
五.JUnit Annotation(註解)
還記得上邊建立測試類的時候出現了setUp和tearDown兩個方法嗎?分別對應@Before和@After這兩個註解。實際上根據JUnit框架的設計,每個單元測試方法可以簡單劃分為:
- setUp 對應 @Before註解的方法
- test 對應 @Test註解的方法
- tearDown 對應 @After註解的方法
如果建立時勾選這兩個方法,
則會生成:
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
注意看看@Test註解的註釋,可以看到,它可以接受兩個引數,
- 一個是預期異常
- 一個是超時時間
//預期異常,不報錯(如果不出現異常則報錯)
@Test(expected=IndexOutOfBoundsException.class)
public void outOfBounds() {
new ArrayList().get(1);
}
//超時報錯
@Test(timeout=100)
public void infinity() {
while(true);
}
//這種情況要小心,注意誤差問題,有可能正確,有可能錯誤
@Test(timeout=100)
public void sleep100() {
Thread.sleep(100);
}
註解名 | 含義 |
---|---|
@Test | 表示此方法為測試方法 |
@Before | 在每個測試方法前執行,可做初始化操作 |
@After | 在每個測試方法後執行,可做釋放資源操作 |
@Ignore | 忽略的測試方法 |
@BeforeClass | 在類中所有方法前執行。此註解修飾的方法必須是static void |
@AfterClass | 在類中最後執行。此註解修飾的方法必須是static void |
@RunWith | 指定該測試類使用某個執行器 |
@FixMethodOrder | 指定測試類中方法的執行順序 |
@Rule | 重新制定測試類中方法的行為 |
執行順序:@BeforeClass –> @Before –> @Test –> @After –> @AfterClass
@Rule用法
還記得一開始我們在@Before
與@After
註解的方法中加入”開始測試”、“結束測試”的提示資訊嗎?假如我們一直需要這樣的提示,那是不是需要每次在測試類中去實現它。這樣就會比較麻煩。這時你就可以使用@Rule
來解決這個問題,它甚至比@Before
與@After
還要強大。
自定義@Rule
很簡單,就是實現TestRule
介面,實現apply
方法。程式碼如下:
public class MyRule implements TestRule {
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
String classNmae=description.getClassName();//獲取測試類名
String methodName=description.getMethodName();//獲取測試方法名
System.out.println(classNmae+"類下的"+methodName+"方法,開始測試");
base.evaluate(); //執行的測試方法
System.out.println(classNmae+"類下的"+methodName+"方法,結束測試");
}
};
}
}
執行結果