Search code examples
javajunithamcrest

Hamcrest IsNot matcher together with a wrapped custom matcher - describeMismatch doesn't work as expected


Recently I've made a custom matcher for the jaxb generated element and came upon this scenario:

Preconditions:

  • I have a custom Matcher extending BaseMatcher with overriden methods describeTo and describeMismatch (and of course matches..)
  • I'm using assertThat( actualObject, not( myMatchersStaticRunMethod( expectedObject ) )

When assertion fails, in result I have:

Expected: not myMatchersDescribeToDescription
but: isNotCoreMatcherDescribeMismatchDescription

Digging in the code of org.hamcrest.core.IsNot class I can see that describeTo is implemented properly (ie. delegates gathering of description to a wrapped matcher), but describeMismatch is not overriden hence the BaseMatcher's version is used.

Imho it's a faulty behavior, because mismatch should be taken from the wrapped matcher as well. What do you think, guys?


Solution

  • I don't know why this was voted down. I agree it is faulty behaviour. Seems similar to this issue

    You can fix it only by creating your own custom "notD" matcher, its a copy of the IsNot matcher adding the Override for describeMismatch:

    import org.hamcrest.BaseMatcher;
    import org.hamcrest.Description;
    import org.hamcrest.Factory;
    import org.hamcrest.Matcher;
    
    import static org.hamcrest.core.IsEqual.equalTo;
    
    
    /**
     * Calculates the logical negation of a matcher.
     */
    public class IsNotDescribing<T> extends BaseMatcher<T>  {
        private final Matcher<T> matcher;
    
        public IsNotDescribing(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);
        }
    
        // use the matcher to describe its mismatch
        @Override
        public void describeMismatch(Object item, Description description) {
            matcher.describeMismatch(item, description);
        }
    
        @Factory
        public static <T> Matcher<T> notD(Matcher<T> matcher) {
            return new IsNotDescribing<T>(matcher);
        }
    
        @Factory
        public static <T> Matcher<T> notD(T value) {
            return notD(equalTo(value));
        }
    }