一察滑、Hamcrest是什么?
Hamcrest is a library of matchers, which can be combined in to create flexible expressions of intent in tests.
Hamcrest 是一個(gè)為了測(cè)試為目的绑改,且能組合成靈活表達(dá)式的匹配器類庫(kù)。
二、為什么要用Hamcrest匹配器框架
Hamcrest的目標(biāo)是使測(cè)試盡可能的提高可讀性.例如is()方法其實(shí)就是equalTo()的包裝方法.
三涉瘾、常用方法介紹
@Test
public void testHamcrestMatchers() {
// 核心匹配
// allOf: 所有條件都必須滿足,相當(dāng)于&&
assertThat("myname", allOf(startsWith("my"), containsString("name")));
// anyOf: 其中一個(gè)滿足就通過(guò)捷兰, 相當(dāng)于||
assertThat("myname", anyOf(startsWith("na"), containsString("name")));
// both: &&
assertThat("myname", both(containsString("my")).and(containsString("me")));
// either: 兩者之一
assertThat("myname", either(containsString("my")).or(containsString("you")));
// everyItem: 每個(gè)元素都需滿足特定條件
assertThat(Arrays.asList("my", "mine"), everyItem(startsWith("m")));
// hasItem: 是否有這個(gè)元素
assertThat(Arrays.asList("my", "mine"), hasItem("my"));
// hasItems: 包含多個(gè)元素
assertThat(Arrays.asList("my", "mine", "your"), hasItems("your", "my"));
// is: is(equalTo(x))或is(instanceOf(clazz.class))的簡(jiǎn)寫
assertThat("myname", is("myname"));
assertThat("mynmae", is(String.class));
// anything(): 任何情況下立叛,都匹配正確
assertThat("myname", anything());
// not: 否為真,相當(dāng)于贡茅!
assertThat("myname", is(not("you")));
// nullValue(): 值為空
String str = null;
assertThat(str, is(nullValue()));
// notNullValue(): 值不為空
String str2 = "123";
assertThat(str2, is(notNullValue()));
// 字符串匹配
// containsString:包含字符串
assertThat("myname", containsString("na"));
// stringContainsInOrder: 順序包含秘蛇,“my”必須在“me”前面
assertThat("myname", stringContainsInOrder(Arrays.asList("my", "me")));
// endsWith: 后綴
assertThat("myname", endsWith("me"));
// startsWith: 前綴
assertThat("myname", startsWith("my"));
// isEmptyString(): 空字符串
assertThat("", isEmptyString());
// equalTo: 值相等, Object.equals(Object)
assertThat("myname", equalTo("myname"));
assertThat(new String[] {"a", "b"}, equalTo(new String[] {"a", "b"}));
// equalToIgnoringCase: 比較時(shí)顶考,忽略大小寫
assertThat("myname", equalToIgnoringCase("MYNAME"));
// equalToIgnoringWhiteSpace: 比較時(shí)赁还, 首尾空格忽略, 比較時(shí)中間用單個(gè)空格
assertThat(" my \t name ", equalToIgnoringWhiteSpace(" my name "));
// isOneOf: 是否為其中之一
assertThat("myname", isOneOf("myname", "yourname"));
// isIn: 是否為其成員
assertThat("myname", isIn(new String[]{"myname", "yourname"}));
// toString() 返回值校驗(yàn)
assertThat(333, hasToString(equalTo("333")));
// 數(shù)值匹配
// closeTo: [operand-error, operand+error], Double或BigDecimal類型
assertThat(3.14, closeTo(3, 0.5));
assertThat(new BigDecimal("3.14"), is(closeTo(new BigDecimal("3"), new BigDecimal("0.5"))));
// comparesEqualTo: compareTo比較值
assertThat(2, comparesEqualTo(2));
// greaterThan: 大于
assertThat(2, greaterThan(0));
// greaterThanOrEqualTo: 大于等于
assertThat(2, greaterThanOrEqualTo(2));
// lessThan: 小于
assertThat(0, lessThan(2));
// lessThanOrEqualTo: 小于等于
assertThat(0, lessThanOrEqualTo(0));
// 集合匹配
// array: 數(shù)組長(zhǎng)度相等且對(duì)應(yīng)元素也相等
assertThat(new Integer[]{1, 2, 3}, is(array(equalTo(1), equalTo(2), equalTo(3))));
// hasItemInArray: 數(shù)組是否包含特定元素
assertThat(new String[]{"my", "you"}, hasItemInArray(startsWith("y")));
// arrayContainingInAnyOrder驹沿, 順序無(wú)關(guān)艘策,長(zhǎng)度要一致
assertThat(new String[]{"my", "you"}, arrayContainingInAnyOrder("you", "my"));
// arrayContaining: 順序,長(zhǎng)度一致
assertThat(new String[]{"my", "you"}, arrayContaining("my", "you"));
// arrayWithSize: 數(shù)組長(zhǎng)度
assertThat(new String[]{"my", "you"}, arrayWithSize(2));
// emptyArray: 空數(shù)組
assertThat(new String[0], emptyArray());
// hasSize: 集合大小
assertThat(Arrays.asList("my", "you"), hasSize(equalTo(2)));
// empty: 空集合
assertThat(new ArrayList<String>(), is(empty()));
// isIn: 是否為集合成員
assertThat("myname", isIn(Arrays.asList("myname", "yourname")));
// Map匹配
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("name", "john");
// hasEntry: key && value匹配
assertThat(myMap, hasEntry("name", "john"));
// hasKey: key匹配
assertThat(myMap, hasKey(equalTo("name")));
// hasValue: value匹配
assertThat(myMap, hasValue(equalTo("john")));
}
詳細(xì)請(qǐng)看: Hamcrest API
四甚负、自定義Hamcrest匹配器
1.通過(guò)FeatureMatcher自定義Hamcrest匹配器
創(chuàng)建Hamcrest匹配器
我們自定義一個(gè)為String提供長(zhǎng)度的匹配器,需要利用FeatureMatcher類,封裝一個(gè)現(xiàn)有的匹配器,用來(lái)決定給定的被測(cè)對(duì)象的哪個(gè)字段匹配,并且提供豐富的錯(cuò)誤信息.FeatureMatcher的構(gòu)造函數(shù)有下列參數(shù):
- 我們想要包裝的匹配器
- 對(duì)我們測(cè)試的功能的描述(在錯(cuò)誤信息會(huì)有體現(xiàn))
- 測(cè)試功能的名字(在錯(cuò)誤信息會(huì)有體現(xiàn))
我們必須重寫featureValueOf(T actual),它的返回值將傳入matchesSafely()方法進(jìn)行匹配判斷.
public static Matcher<String> length(Matcher<? super Integer> matcher) {
return new FeatureMatcher<String, Integer>(matcher,
"a String of length that", "length") {
@Override
protected Integer featureValueOf(String actual) {
return actual.length();
}
};
}
測(cè)試
使用你剛才創(chuàng)建的自定義匹配器驗(yàn)證"Gandalf"的長(zhǎng)度為8
@Test
public void fellowShipOfTheRingShouldContainer7() {
assertThat("Gandalf", length(is(8)));
}
使用TypeSafeMatcher自定義匹配器
我們可以對(duì)TypeSafeMatcher進(jìn)行擴(kuò)展.與BaseMatcher相比TypeSafeMatcher可以自動(dòng)的檢查null值, 在被委派到matchesSafely()方法之前檢查類型并進(jìn)行適當(dāng)?shù)霓D(zhuǎn)換.下面定義了一個(gè)檢查一個(gè)字符串是否匹配正則關(guān)系的匹配器.
public class RegexMatcher extends TypeSafeMatcher<String> {
private final String regex;
public RegexMatcher(final String regex) {
this.regex = regex;
}
@Override
public void describeTo(final Description description) {
description.appendText("matches regular expression=`" + regex + "`");
}
@Override
public boolean matchesSafely(final String string) {
return string.matches(regex);
}
// matcher method you can call on this matcher class
public static RegexMatcher matchesRegex(final String regex) {
return new RegexMatcher(regex);
}
}
測(cè)試
@Test
public void testRegularExpressionMatcher() throws Exception {
String s ="aaabbbaaaa";
assertThat(s, RegexMatcher.matchesRegex("a*b*a*"));
}
自定義組合匹配器
為什么要自定義組合匹配器
Hamcrest有內(nèi)置的組合匹配器,但是它的可讀性太差!
下面就是一個(gè)案例:
@Test
public void testCombining() {
List<Integer> list = new ArrayList<>();
assertThat(list, both(hasSize(1)).and(contains(42)));
}
可讀性差,無(wú)法準(zhǔn)確描述錯(cuò)誤信息.
創(chuàng)建自定義組合匹配器
我們可以繼承BaseMatchers類使用它提供對(duì)外連接的方法(matches),本身再提供一個(gè)添加方法(add).將匹配器鏈接起來(lái).并保存在集合中.
public class MatcherCombinator<T> extends BaseMatcher<T> {
private final List<Matcher<? super T>> matchers = new ArrayList<>();
private final List<Matcher<? super T>> failedMatchers = new ArrayList<>();
private MatcherCombinator(final Matcher<? super T> matcher) {
matchers.add(matcher);
}
public MatcherCombinator<T> and(final Matcher<? super T> matcher) {
matchers.add(matcher);
return this;
}
@Override
public boolean matches(final Object item) {
boolean matchesAllMatchers = true;
for (final Matcher<? super T> matcher : matchers) {
if (!matcher.matches(item)) {
failedMatchers.add(matcher);
matchesAllMatchers = false;
}
}
return matchesAllMatchers;
}
@Override
public void describeTo(final Description description) {
description.appendValueList("\n", " " + "and" + "\n", "", matchers);
}
@Override
public void describeMismatch(final Object item, final Description description) {
description.appendText("\n");
for (Iterator<Matcher<? super T>> iterator = failedMatchers.iterator(); iterator.hasNext();) {
final Matcher<? super T> matcher = iterator.next();
description.appendText("Expected: <");
description.appendDescriptionOf(matcher).appendText(" but ");
matcher.describeMismatch(item, description);
if (iterator.hasNext()) {
description.appendText(">\n");
}
}
}
public static <LHS> MatcherCombinator<LHS> matches(final Matcher<? super LHS> matcher) {
return new MatcherCombinator<LHS>(matcher);
}
}
測(cè)試
@Test
public void testCustomCombining() {
List<Integer> list = new ArrayList<>();
assertThat(list, MatcherCombinator.matches(hasSize(1)).and(contains(42)));
}