
上QQ阅读APP看书,第一时间看更新
2.3 自定义对象匹配器
通过前面几节的学习,我们了解到,Hamcrest提供了非常丰富的对象匹配器。大多数时候,这些对象匹配器足以应对日常工作的需要。如果你觉得这些还不能满足自己的要求,则可以通过自定义对象匹配器的方式进行扩展,本节将通过字符串正则匹配的示例来演示如何自定义Hamcrest的对象匹配器。示例代码如程序代码2-7所示。
程序代码2-7 RegexMatcher.java
package com.wangwenjun.cicd.chapter02; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatcher<E extends CharSequence> extends TypeSafeMatcher<String> { //传入的正则表达式。 private E expected; public RegexMatcher(E expected) { this.expected = expected; } //正则表达式既可以是String类型,也可以是StringBuilder/StringBuffer类型。 @Override protected boolean matchesSafely(String item) { String reg = ""; if (expected instanceof String) { reg = (String) expected; } else if (expected instanceof StringBuffer) { reg = ((StringBuffer) expected).toString(); } else if (expected instanceof StringBuilder) { reg = ((StringBuilder) expected).toString(); } else { return false; } //进行正则匹配。 final Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(item); return matcher.matches(); } @Override public void describeTo(Description description) { //期望结果信息描述。 description.appendText("matched the regex: ").appendValue(expected); } @Override public void describeMismatchSafely(String item, Description mismatchDescription) { //匹配失败时会输出如下描述信息。 mismatchDescription.appendText("String ") .appendValue(item) .appendText(" missed match regex: ") .appendValue(expected); } //静态工厂方法。 public static <T extends CharSequence> RegexMatcher match(T t) { return new RegexMatcher<T>(t); } }
开发一个对象匹配器还是很容易的,只需要继承TypeSafeMatcher类,重写三个简单方法即可,其中,matchesSafely()方法比较重要,它能直接决定对象匹配器成功与否。下面就来验证我们自定义的对象匹配器,首先,编写一个能够正确运行的匹配断言,代码如下。
@Test public void testRegexMatcher() { assertThat("test zip code", "123456", match("\\d{6}")); assertThat("test zip code", "123456", is(match("\\d{6}"))); assertThat("test zip code", "123456", not(not(match("\\d{6}")))); assertThat("test zip code", "123456", match(new StringBuilder("\\d{6}"))); assertThat("test ip v4 address", "127.0.0.1", match( new StringBuffer("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"))); }
这里需要注意的一点是,上面的代码不仅可以直接使用match静态方法,而且可以使用Hamcrest提供的语法糖is()方法,其中甚至还使用了双重否定语句not(not(match(...)))。运行单元测试方法,结果没有任何问题,那么匹配失败的情况又是怎样的呢?下面是一个不能正确匹配断言的单元测试方法(请重点关注错误提示信息)。
@Test public void testRegexMatcherFailed() { //断言失败。 assertThat("test zip code", "123456", match("\\d{5}")); }
当断言失败时,RegexMatcher中的describe方法可以在输出中提供详细的错误描述信息,这非常便于定位问题所在,如图2-8所示。

图2-8 自定义对象匹配器断言失败时的错误提示