判断是不是,有没有
用户入口
MatcherAssert
中的assertThat
系列方法是Hamcrest的入口
用户可以直接给出一个布尔表达式,这种没什么好说,核心还是在于Matcher的使用
Matcher描述了期待值,它会判断实际值actual
是否满足
如果不满足,assertThat
会给出原因描述
MatcherAssert.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class MatcherAssert { public static <T> void assertThat(T actual, Matcher<? super T> matcher) { assertThat("", actual, matcher); }
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) { if (!matcher.matches(actual)) { Description description = new StringDescription(); description.appendText(reason) .appendText("\nExpected: ") .appendDescriptionOf(matcher) .appendText("\n but: "); matcher.describeMismatch(actual, description); throw new AssertionError(description.toString()); } }
public static void assertThat(String reason, boolean assertion) { if (!assertion) { throw new AssertionError(reason); } } }
|
匹配器职责和框架实现
匹配器的职责比较直观:判断+解释
判断对象是不是匹配是最最核心的功能
同时解释也是必要的,每个匹配器实现SelfDescribing
接口描述自己的功能,在判断不匹配时也会给出原因
SelfDescribing.java Matcher.java1 2 3 4 5 6 7 8
| public interface SelfDescribing { void describeTo(Description description); } public interface Matcher<T> extends SelfDescribing { boolean matches(Object item);
void describeMismatch(Object item, Description mismatchDescription); }
|
实际的比较器有个公共基类,主要负责一些描述信息的处理
BaseMatcher.java1 2 3 4 5 6 7 8 9 10 11
| public abstract class BaseMatcher<T> implements Matcher<T> { @Override public void describeMismatch(Object item, Description description) { description.appendText("was ").appendValue(item); }
@Override public String toString() { return StringDescription.toString(this); } }
|
匹配器实现
以最常见的判断相等的IsEqual
匹配器为例
判断相等的实现比较直观,就是特殊考虑了数组相等,此外就是原生的equals
函数
亮点在静态的工厂方法equalTo
。日常基本都是使用静态方法,静态方法创建了匹配器实例。
静态方法还取了一个语义上更好的名字,为以后的匹配器包装做准备。
IsEqual.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class IsEqual<T> extends BaseMatcher<T> { private final Object expectedValue;
public IsEqual(T equalArg) { expectedValue = equalArg; }
@Override public boolean matches(Object actualValue) { return areEqual(actualValue, expectedValue); }
@Override public void describeTo(Description description) { description.appendValue(expectedValue); }
private static boolean areEqual(Object actual, Object expected) { if (actual == null) { return expected == null; }
if (expected != null && isArray(actual)) { return isArray(expected) && areArraysEqual(actual, expected); }
return actual.equals(expected); }
@Factory public static <T> Matcher<T> equalTo(T operand) { return new IsEqual<T>(operand); } }
|
匹配器包装
有的匹配器支持与其他匹配器组合,以取反操作IsNot
匹配器为例
典型的装饰器模式
,内部包含了其他匹配器实例,判断时加入取反逻辑
同样提供静态的工厂方法,接受匹配器,返回匹配器,使链式包装成为可能
IsNot.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class IsNot<T> extends BaseMatcher<T> { private final Matcher<T> matcher;
public IsNot(Matcher<T> matcher) { this.matcher = matcher; }
@Override public boolean matches(Object arg) { return !matcher.matches(arg); }
@Override public void describeTo(Description description) { description.appendText("not ").appendDescriptionOf(matcher); }
@Factory public static <T> Matcher<T> not(Matcher<T> matcher) { return new IsNot<T>(matcher); }
@Factory public static <T> Matcher<T> not(T value) { return not(equalTo(value)); } }
|
可用匹配器
匹配器类 |
静态方法 |
含义 |
终端匹配器 |
IsEqual |
equalTo |
相等 |
IsAnything |
anything |
总是满足 |
IsInstanceOf |
instanceOf |
是类的实例 |
IsNull |
nullValue |
是null |
简写 |
notNullValue |
等同not(nullValue(…)) |
IsSame |
sameInstance |
同一对象 |
StringContains |
containsString |
字符串包含 |
StringStartsWith |
startsWith |
字符串包含前缀 |
StringEndsWith |
endsWith |
字符串包含后缀 |
组合单个匹配器 |
Is |
is |
是 |
简写 |
isA |
等同is(instanceOf(…)) |
IsNot |
not |
否 |
Every |
everyItem |
集合每个满足 |
IsCollectionContaining |
hasItem |
集合包含 |
简写 |
hasItem |
等同hasItem(equalTo(…)) |
组合多个匹配器 |
AllOf |
allOf |
满足所有 |
AnyOf |
anyOf |
满足任意 |
IsCollectionContaining |
hasItems |
集合包含多个 |
简写 |
hasItems |
allOf(hasItem(…),hasItem(…)…) |
例子
即使写的条件比较复杂,可读性还可以
1 2
| MatcherAssert.assertThat(Arrays.asList("1", "2"), allOf(everyItem(not(startsWith("0"))), hasItem(equalTo("1"))));
|