在單元測試過程結(jié)束后买决,我們期望編譯器可以直接告訴我們是 fail 還是 success莲绰。那么如何判斷某個 case 是否通過,就尤為重要覆旭,如果只是簡單的使用 assertEqual退子、assertFalse、assertNull型将、assertNotEquals寂祥、assertSame,那么很多情況就判斷不了七兜,比如判斷某個集合是否包含某個元素丸凭,某個字符串是否以"Man"開頭,這個時候我們就需要搬出匹配器了。
匹配器簡介
其實匹配器就是內(nèi)部采用了特定的算法惜犀,來實現(xiàn)特定的業(yè)務判斷铛碑,比如 startsWith("Man")
返回的就是的就是一個用來判斷某個字符串是否以Man
開頭的字符串。日常中還是有很多匹配器是很常見的虽界,所以就有這么一個包含大量常見匹配器的框架汽烦,叫做Hamcrest
,該框架結(jié)合Junit
用起來確實很棒莉御,所以從Junit 4.11
開始撇吞,Junit
已經(jīng)默認依賴了Hamcrest
,以Junit4.12
為例子礁叔,內(nèi)部依賴的就是Hamcrest-core:1.3
牍颈。
如何在單元測試中使用Hamcrest
呢?其實很簡單琅关,只要使用以下這個斷言即可
public static <T> void assertThat(T actual, Matcher<? super T> matcher);
集成 Hamcrest
上面介紹了自從Junit 4.11
開始煮岁,就已經(jīng)自動依賴了,但是為什么本節(jié)還要講集成呢涣易?原因有下面兩點
-
默認集成的是
Hamcrest-core:1.3
画机,常用的匹配器方法被封裝在幾十個類中,這樣我們使用一些靜態(tài)方法會很麻煩都毒,需要一個一個導包色罚,如圖一所示:
圖一.png core
只是包含了最常用的一些匹配器,像數(shù)組账劲、字典、數(shù)值之類的大部分匹配器是沒有的金抡,但這類匹配器我們?nèi)粘i_發(fā)中使用到的場景也不少瀑焦。
所以我們需要集成全部的匹配器,我們在app/build.gradle
中添加如下依賴
testImplementation 'junit:junit:4.12'
testImplementation 'org.hamcrest:hamcrest-all:1.3'
然后在使用到的單元測試類或者測試基類中導入所有匹配器梗肝,這樣我們就不需要想圖一一樣每用一個需要導入一個榛瓮,而且你還需要準確的記得每個匹配器的名字,不然是沒有智能提示的巫击。
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
Hamcrest的使用
Hamcrest
的匹配器用起來確實很簡單禀晓,所以直接上常用幾十匹配器的例子,大多數(shù)通過看名字就知道匹配器的作用坝锰,一些可能造成誤解的我也都寫了注釋粹懒,有時間的伙伴可以敲一遍或者瀏覽一下,有個印象顷级,等用到的時候可以再查看凫乖。
// hamcrest-core
@Test
public void testHamCrest() {
//JUnit 4.11 and later 自動集成 core
//基礎操作
//1. 字符串相關
assertThat("myValue", startsWith("my"));
assertThat("myValue", containsString("Val"));
assertThat("myValue", endsWith("e"));
assertThat("myValue", equalTo("myValue"));
assertThat("myValue", anything()); //怎樣都是通過
assertThat("myValue", anything("怎樣都是通過"));
//2. 定義相關,比如某位是什么,不是什么帽芽,等于什么,不等于什么
assertThat(1, equalTo(1));
assertThat("myValue", instanceOf(String.class));
assertThat(1, not(2));
assertThat(null, nullValue());
assertThat("myValue", notNullValue());
assertThat("myValue", sameInstance("myValue")); //和 theInstance(T)一樣
//3. 集合相關 Iterable
//3.1 everyItem 每個 item 都要符合條件
assertThat(Arrays.asList("bar", "baz"), everyItem(startsWith("ba")));
//3.2 hasItem 至少有一個 item 都符合條件删掀,或者集合中有這個 item,參數(shù)可以是 T 也可以是匹配器
assertThat(Arrays.asList("foo", "bar"), hasItem("bar"));
assertThat(Arrays.asList("foo", "bar"), hasItem(startsWith("fo")));
//3.3 hasItems 是hasItem復數(shù)版本导街,支持多 T 類型參數(shù)和多匹配器參數(shù)
assertThat(Arrays.asList("foo", "bar", "baz"), hasItems("baz", "foo"));
assertThat(Arrays.asList("foo", "bar", "baz"), hasItems(endsWith("z"), endsWith("o")));
//組合匹配器披泪,一般都支持多個參數(shù),雖然下面的提供的是使用兩個參數(shù)的例子
//1. allOf 全部條件都需要滿足
assertThat("myValue", allOf(startsWith("my"), containsString("Val")));
//2. anyOf 滿足其中一個條件即可
assertThat("myValue", anyOf(startsWith("foo"), containsString("Val")));
//3. both().and() 滿足兩個條件搬瑰,為 allOf 的真子集
assertThat("fab", both(containsString("a")).and(containsString("b")));
//4. either().or() 滿足一個條件款票,為 anyOf 的真子集
assertThat("fab", either(containsString("a")).or(containsString("b")));
//輔助斷言,對于機器來說沒什么用跌捆,只是讓語句讀起來更加像自然語言
//1. describedAs 增加斷言輔助描述徽职,增強可讀性,一旦斷言不通過佩厚,將直接打印描述內(nèi)容到控制臺姆钉。
//比如下面這個例子,看完之后我們就知道這個斷言輔助描述是想告訴我們?yōu)槭裁雌诖闹凳?.
//等同于 assertThat(2, equalTo(2));
assertThat(2, describedAs("1 + 1 must equal 2", equalTo(2)));
//2. is 又是一個語法糖抄瓦,增加可讀性而已
assertThat("foo", is(equalTo("foo")));
//2.0 如果里面的匹配器是 equalTo潮瓶,則可以簡寫
assertThat("foo", is("foo"));
//3. isA 又是一個語法糖,不過參數(shù)只能是 Class<T>
//其實就是assertThat("foo", is(instanceOf(String.class)))的簡寫
assertThat("foo", isA(String.class));
//自定義匹配器, 繼承自CustomMatcher,實現(xiàn) matches 方法即可
Matcher<String> aNonEmptyString = new CustomMatcher<String>("a non empty string") {
public boolean matches(Object object) {
return (object instanceof String) && !((String) object).isEmpty();
}
};
assertThat("foo", aNonEmptyString);
}
以上是core
部分钙姊,意思就是使用Junit 4.12
自帶依賴的Hamcrest
即可使用毯辅,不過需要手動導包,而且是很多包煞额,下面補充一下其它常用的匹配器思恐,屬于core
之外的了。
// hamcrest-all
@Test
public void hamcrestAll() throws Exception {
//array,針對數(shù)組每一項進行測試 each matcher[i] is satisfied by array[i]膊毁,條件成立僅當匹配器個數(shù)等于數(shù)組元素個數(shù)胀莹,且每個匹配器都通過
//數(shù)組類型
assertThat(new Integer[]{1,2,3}, is(array(equalTo(1), equalTo(2), equalTo(3))));
//包含所有內(nèi)容,不需要按照順序婚温,和array不一樣
assertThat(new Integer[]{1,2,3}, arrayContainingInAnyOrder(3, 2, 1));
assertThat(new String[] {"foo", "bar"}, hasItemInArray(startsWith("ba")));
assertThat(new Integer[]{1,2,3}, arrayWithSize(3)); //對應 Collection 類型的 hasSize()
assertThat(new String[0], emptyArray()); //對應 Collection 的 empty()
//Iterable類型也有上面相應的 API描焰,下面舉兩個例子
assertThat(Arrays.asList("foo", "bar"), hasSize(2));
assertThat(new ArrayList<String>(), is(empty()));
//map類型
HashMap<String, String> map = new HashMap<>();
map.put("bar", "foo");
map.put("name", "Mango");
//是否包含特定鍵值對
assertThat(map, hasEntry("bar", "foo"));
assertThat(map, hasEntry(equalTo("bar"), equalTo("foo")));
assertThat(map, hasKey(equalTo("bar")));
assertThat(map, hasValue(equalTo("foo")));
//期望的值是否屬于某個集合
assertThat("foo", isIn(Arrays.asList("bar", "foo")));
//double 類型
//誤差在正負0.04內(nèi)算通過
assertThat(1.03, is(closeTo(1.0, 0.04)));
assertThat(2, greaterThan(1));
assertThat(1, greaterThanOrEqualTo(1));
assertThat(1, lessThan(2));
assertThat(1, lessThanOrEqualTo(1));
//text 類型
assertThat("Foo", equalToIgnoringCase("FOO"));
assertThat(" my\tfoo bar ", equalToIgnoringWhiteSpace(" my foo bar"));
assertThat("", isEmptyString());
assertThat(null, isEmptyOrNullString());
}
例子到這里就結(jié)束了,最后再附上官方文檔栅螟。