如何测试自定义hamcrest匹配器?

时间:2013-08-21 05:33:05

标签: unit-testing junit matcher hamcrest

我正在编写一些自定义匹配器来简化junit断言。其中大多数扩展了TypeSafeMatcher,所以我只需要覆盖三种方法:

public class NoneConstraintViolationMatcher<T> extends
    TypeSafeMatcher<Set<ConstraintViolation<T>>> {

    @Override
    public void describeTo(Description description) {
        description.appendText("None constraint violations found");
    }

    @Override
    protected void describeMismatchSafely(Set<ConstraintViolation<T>> item,
        Description mismatchDescription) {
        mismatchDescription.
            appendText("Unexpected constraint violation found, but got ");
        mismatchDescription.appendValueList("", ",", "", item);
    }

    @Override
    protected boolean matchesSafely(Set<ConstraintViolation<T>> item) {
        return item.isEmpty();
    }
}

我的问题是如何测试它们?我目前的解决方案是

public class NoneConstraintViolationMatcherUnitTests {

    private NoneConstraintViolationMatcher<Object> matcher = 
        new NoneConstraintViolationMatcher<Object>();

    @Test
    public void returnsMatchedGivenNoneConstraintViolations() throws Excetpion {
         assertTrue(matcher.matches(.....));
    }  

    @Test
    public void returnsMismatchedGivenSomeConstraintViolations() throws Excetpion {
         assertThat(matcher.matches(.....), is(false));
    }        

    @Test
    public void returnsConstraintViolationsFoundWhenMismatched()
        throws Exception {

        StringBuilder out = new StringBuilder();
        //I don't find anything could be used to assert in description

        StringDescription description = new StringDescription(out);

        matcher.describeMismatch(..someCvx, description);

        assertThat(out.toString(), 
            equalTo("Unexpected constraint violation found, but got "));
    }
 }

我想到的另一个解决方案是编写junit测试并使用@Rule ExpectedException(将handleAssertionError设置为true)。

你们如何测试匹配器?提前谢谢。

3 个答案:

答案 0 :(得分:2)

我正在使用assertThat来测试匹配功能。

@Test
public void returnsMatchedGivenNoneConstraintViolations() throws Excetpion {
     assertThat(someObject, matcher);
}  

@Test
public void returnsMismatchedGivenSomeConstraintViolations() throws Excetpion {
     assertThat(someObject, not(matcher));
}   

答案 1 :(得分:0)

要回答有关测试说明的后续问题,我认为没有必要。我认为写这样的returnsConstraintViolationsFoundWhenMismatched测试就足够了

@Test(expected=Exception.class)
public void returnsConstraintViolationsFoundWhenMismatched() {
  // Use the matcher in a way that causes an exception to be thrown
}

重要的验证是抛出了适当的异常。测试实际消息的内容,而不是向测试套件添加任何值。您可以相信Hamcrest库正在使用您附加到描述中的文本做正确的事情。

答案 2 :(得分:0)

我编写了在断言中调用匹配器方法的单元测试。我认为@Factory对大多数人都有帮助。

这是匹配器:

public class ContainsIgnoringCaseMatcher extends TypeSafeMatcher<String> {

  private List<String> segmentsToMatch;

  public ContainsIgnoringCaseMatcher(String subString, String... additional) {
    Preconditions.checkArgument(subString != null && !subString.isEmpty());
    segmentsToMatch = Lists.newArrayList(subString);

    if (additional != null) {
      segmentsToMatch.addAll(Arrays.asList(additional));
    }
  }

  @Override
  protected boolean matchesSafely(String fullText) {
    return segmentsToMatch.stream()
        .allMatch(v -> StringUtils.containsIgnoreCase(fullText, v));

  }

  @Override
  public void describeTo(Description description) {
    description.appendText(String.format("containing each %s", segmentsToMatch));
  }

  @Factory
  public static Matcher<String> containsIgnoringCase(String subString, String... additionalStrings) {
    return new ContainsIgnoringCaseMatcher(subString, additionalStrings);
  }

  @Factory
  public static Matcher<String> containsIgnoringCase(List<String> subStrings) {
    String first = subStrings.get(0);

    if (subStrings.size() > 1) {
      List<String> subList = subStrings.subList(1, subStrings.size());
      String[] additional = subList.toArray(new String[subList.size() - 1]);

      return new ContainsIgnoringCaseMatcher(first, additional);
    } else {
      return new ContainsIgnoringCaseMatcher(first);
    }
  }
}

单元测试方法如下所示:

  @Test
  public void shouldAllowSubstringMatchOfExpected() {
    assertThat("EXPECTED_ITEM", containsIgnoringCase("expected_"));
  }

  @Test(expected = AssertionError.class)
  public void shouldNotAllowSubstringMatchOfFound() {
    assertThat("EXPECTED_", containsIgnoringCase("EXPECTED_ITEM"));
  }

  @Test
  public void shouldAllowNegatedAssert() {
    assertThat("EXPECTED_", not(containsIgnoringCase("EXPECTED_ITEM")));
  }

  @Test(expected = AssertionError.class)
  public void shouldNotAllowMismatch() {
    assertThat("ab", containsIgnoringCase("ba"));
  }