Search code examples
junithamcrest

Static import of JUnitMatchers.containsString works but not CoreMatchers.containsString


I have a fairly huge code base. One of the test classes is using an import statement which is failing during compilation for the following static import. It fails on the import statement as well as where the actual call to constainsString() is made.

import static org.hamcrest.CoreMatchers.containsString;

The exact error is as follows:

[ERROR] MyClassName:[...] error: cannot find symbol
[ERROR] symbol:   method containsString(String)
[ERROR] location: class MyClassName

Changing the above static import to JUnitMatchers.containsString fixes the issue:

import static org.junit.matchers.JUnitMatchers.containsString;

My effective pom.xml has the following related dependencies:

junit:jUnit:4.11
org.hamcrest:hamcrest-all:1.1
org.mockito:mockito-all:1.9.5

So, the questions really are

  1. Why is JUnitMatchers.containsString successful, when in reality it is deprecated in favor of CoreMatchers.containsString?
  2. If it's picking up the wrong version of any of the libraries, how could we make maven use the right version?

Solution

  • Hamcrest 1.1 does not have the containsString method. Could not find the sources (apparently, as per the comment on this commit, previously the class was being generated), but we can use the decompiled class:

    public class CoreMatchers {
        public static <T> Matcher<T> is(Matcher<T> matcher) {
            return Is.is(matcher);
        }
    
        public static <T> Matcher<T> is(T value) {
            return Is.is(value);
        }
    
        public static Matcher<Object> is(Class<?> type) {
            return Is.is(type);
        }
    
        public static <T> Matcher<T> not(Matcher<T> matcher) {
            return IsNot.not(matcher);
        }
    
        public static <T> Matcher<T> not(T value) {
            return IsNot.not(value);
        }
    
        public static <T> Matcher<T> equalTo(T operand) {
            return IsEqual.equalTo(operand);
        }
    
        public static Matcher<Object> instanceOf(Class<?> type) {
            return IsInstanceOf.instanceOf(type);
        }
    
        public static <T> Matcher<T> allOf(Matcher... matchers) {
            return AllOf.allOf(matchers);
        }
    
        public static <T> Matcher<T> allOf(Iterable<Matcher<? extends T>> matchers) {
            return AllOf.allOf(matchers);
        }
    
        public static <T> Matcher<T> anyOf(Matcher... matchers) {
            return AnyOf.anyOf(matchers);
        }
    
        public static <T> Matcher<T> anyOf(Iterable<Matcher<? extends T>> matchers) {
            return AnyOf.anyOf(matchers);
        }
    
        public static <T> Matcher<T> sameInstance(T object) {
            return IsSame.sameInstance(object);
        }
    
        public static <T> Matcher<T> anything() {
            return IsAnything.anything();
        }
    
        public static <T> Matcher<T> anything(String description) {
            return IsAnything.anything(description);
        }
    
        public static <T> Matcher<T> any(Class<T> type) {
            return IsAnything.any(type);
        }
    
        public static <T> Matcher<T> nullValue() {
            return IsNull.nullValue();
        }
    
        public static <T> Matcher<T> nullValue(Class<T> type) {
            return IsNull.nullValue(type);
        }
    
        public static <T> Matcher<T> notNullValue() {
            return IsNull.notNullValue();
        }
    
        public static <T> Matcher<T> notNullValue(Class<T> type) {
            return IsNull.notNullValue(type);
        }
    
        public static <T> Matcher<T> describedAs(String description, Matcher<T> matcher, Object... values) {
            return DescribedAs.describedAs(description, matcher, values);
        }
    }
    

    Couldn't find version 1.2 in the central repo, but 1.3 does define that method (and a few others).

    JUnit 4.11 (and 4.12 as well) already references Hamcrest 1.3, but mockito-1.9.5 references v1.1(scroll to dependencies). You can also see this by running mvn dependency:tree:

    [INFO] \- org.mockito:mockito-core:jar:1.9.5:compile
    [INFO]    +- org.hamcrest:hamcrest-core:jar:1.1:compile
    [INFO]    \- org.objenesis:objenesis:jar:1.0:compile
    
    ---------------
    
    [INFO] +- junit:junit:jar:4.11:compile
    [INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:compile
    

    Without seeing the actual pom I can't say how, but somehow v1.1 takes precedence over 1.3, either explicitly, or maybe mockito is declared in your pom before junit. So, you can try swapping the junit and mockito order in your pom, or explicitly defining Hamcrest 1.3 directly to override the transitive version:

    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-all</artifactId>
        <version>1.3</version>
        <scope>test</scope>
    </dependency>