NoSuchMethodError with Hamcrest 1.3 & JUnit 4.11

2019-02-05 10:47发布

Another instance of the NoSuchMethodError for the JUnit & Hamcrest combination. Offending code:

assertThat(dirReader.document(0).getFields(), hasItem(
    new FeatureMatcher<IndexableField, String>(equalTo("Patisnummer"), "Field key", "Field key") {
        @Override
        protected String featureValueOf(IndexableField actual) {
            return actual.name();
        } } ));

Commented lines 152–157 in IndexerTest.java (commit ac72ce)

Causes a NoSuchMethodError (see http://db.tt/qkkkTE78 for complete output):

java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V
at org.hamcrest.FeatureMatcher.matchesSafely(FeatureMatcher.java:43)
at org.hamcrest.TypeSafeDiagnosingMatcher.matches(TypeSafeDiagnosingMatcher.java:55)
at org.hamcrest.core.IsCollectionContaining.matchesSafely(IsCollectionContaining.java:25)
at org.hamcrest.core.IsCollectionContaining.matchesSafely(IsCollectionContaining.java:14)
at org.hamcrest.TypeSafeDiagnosingMatcher.matches(TypeSafeDiagnosingMatcher.java:55)
at org.junit.Assert.assertThat(Assert.java:770)
at org.junit.Assert.assertThat(Assert.java:736)
at indexer.IndexerTest.testIndexContainsField(IndexerTest.java:152)

The setup:

  • JUnit 4.11
  • Hamcrest 1.3
  • Using Maven's surefire plugin (version 2.14), which uses its JUnitCoreProvider
  • Java 7 (OpenJDK)
  • See pom (commit ac72ce)

Background:

A NoSuchMethodError is caused by (compiled) classes that call non existing methods. The specific case of describeMismatch and the JUnit + Hamcrest combination is often caused by an incompatibility between Hamcrest classes included in JUnit and versions of those classes in the Hamcrest library.

Attempts to solve the NoSuchMethodError:

  • The pom contains an explicit dependency on Hamcrest-library 1.3, Hamcrest-core 1.3, and JUnit 4.11, (in that order) as suggested by Garrett Hall in answer to Getting "NoSuchMethodError: org.hamcrest.Matcher.describeMismatch" when running test in IntelliJ 10.5

  • According to the JUnit documentation the JUnit 4.11 Maven dependency does no longer include compiled Hamcrest classes, instead it has a dependency on Hamcrest-core 1.3; so the NoSuchMethodError should not occur.

  • Checking the dependency tree with mvn dependency:tree as suggested by Dan in answer to junit and hamcrest declaration shows the explicit dependencies on Hamcrest 1.3 and JUnit 4.11 and no other dependencies to those files (see http://db.tt/C2OfTDJB for the complete output).

  • In another test the NoSuchMethodError was avoided by using:

    assertThat(
        "Zylab detector not available",
        d.getDetectors(),
        hasItem(Matchers.<Detector>instanceOf(ZylabMetadataXmlDetector.class)));
    

    In lines 120–123 of IndexerTest.java (commit ac72ce) instead of the more obvious:

    assertThat(
        "Zylab detector not available",
        d.getDetectors(),
        hasItem(isA(ZylabMetadataDetector.class));
    

    I'm uncertain whether the explicit type parameter <Detector>, using instanceOf instead of isA, the explicit reference to Hamcrest's Matchers, or a combination of those avoided the NoSuchMethodException; after fiddling around and trying different things it worked.

  • Using explicit type parameters did not solve/avoid the error.

  • Using a class derived from BaseMatcher instead of FeatureMatcher did not solve/avoid the error.

Ideas how fix the NoSuchMethodError?

8条回答
趁早两清
2楼-- · 2019-02-05 11:16

If you are using Eclipse, the "Open Type" tool (CTRL+SHIFT+T) can help you find the problematic package. Just search for the class name (e.g., Description), multiple occurrences of the same class from different JARs are red flags.

查看更多
戒情不戒烟
3楼-- · 2019-02-05 11:22

If you are using Eclipse: For me, in eclipse-> project properties->Java build Path moving mockito-all-1.9.5.jar to the bottom of the 'Order and Export' list did the trick. Just above that I have junit-4.11.jar and above that hamcrest-core-1.3.jar

查看更多
放荡不羁爱自由
4楼-- · 2019-02-05 11:24

What worked for me was to reorder dependencies. Instead of going mockito, junit, I had to put junit, mockito.

Mockito 1.9.5 uses hamcrest 1.1 which is incompatible and causes problems.

查看更多
别忘想泡老子
5楼-- · 2019-02-05 11:32

Using David's tip and How do I split a string on a delimiter in Bash? resulted in the following bash script:

( IFS=":"; for i in `mvn dependency:build-classpath | grep -v '\[INFO\]'`; do jar tf $i | awk "{print \"$i\\t\" \$1}"; done | grep Matcher )

(online at http://www.kaspervandenberg.net/2013/scripts/findDependencyClass.sh)

Which found that dependency JGlobus-Core-2.0.4 has its own versions of org.hamcrest.BaseMatcher, org.hamcrest.CoreMatchers, and org.hamcrest.Matcher.

查看更多
疯言疯语
6楼-- · 2019-02-05 11:37

I solved this jar hell problem in my Gradle project with the code below:

    testCompile (group: 'junit', name: 'junit', version: '4+') {
        exclude group: 'org.hamcrest'
    }
    testCompile ('org.mockito:mockito-core:1+') {
        exclude group: 'org.hamcrest'
    }
    testCompile 'org.hamcrest:java-hamcrest:2.0.0.0'
查看更多
我欲成王,谁敢阻挡
7楼-- · 2019-02-05 11:37

For a project with Gradle as a build tool:

testCompile("junit:junit:4.11") {
     exclude group: 'org.hamcrest', module: 'hamcrest-core'
     exclude group: 'org.hamcrest', module: 'hamcrest-library' 
}
testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
查看更多
登录 后发表回答