1. 程式人生 > >Android 單元測試之JUnit

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+"方法,結束測試");
            }
        };
    }
}

這裡寫圖片描述
執行結果
這裡寫圖片描述