为什么 JUnit 4.12 在使用 @RunWith(SpringJUnit4ClassRunner.class) 时会为 assertEquals 抛出 NoSuchMethodError?

Why does JUnit 4.12 throw a NoSuchMethodError for assertEquals when using @RunWith(SpringJUnit4ClassRunner.class)?

我们的一个 Web 服务最近发生了变化,增加了在我们的测试中使用自动装配 bean 的要求,但是,我无法获得每个人都建议工作的 @RunWith(SpringJUnit4ClassRunner.class) 路由。当我将该注释添加到我的 class 时,我收到了 assertEquals 方法的 "NoSuchMethodError"。

pom.xml 依赖关系:

<properties>
    <jersey.version>2.4.1</jersey.version>
    <spring.version>3.2.3.RELEASE</spring.version>
    <slf4j.version>1.7.2</slf4j.version>
</properties>

<dependencies>

    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>${jersey.version}</version>            
    </dependency>       
    <dependency>
        <groupId>org.glassfish.distributions</groupId>
        <artifactId>web-all</artifactId>
        <version>10.0-build-20080430</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.embedded</groupId>
        <artifactId>gf-embedded-api</artifactId>
        <version>1.0-alpha-4</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>${jersey.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
            </exclusion>            
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
            </exclusion>
        </exclusions>           
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>${jersey.version}</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-mvc</artifactId>
        <version>${jersey.version}</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>

    <!-- Mail libraries-->
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>com.springsource.javax.mail</artifactId>
        <version>1.4.5</version>
        <type>jar</type>
    </dependency>

    <!-- Spring Framework Libraries -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-instrument</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-instrument-tomcat</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-struts</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc-portlet</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- Logging -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>log4j-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jul-to-slf4j</artifactId>
        <version>${slf4j.version}</version>
    </dependency>

    <!-- The AS400 driver and control package -->
    <dependency>
        <groupId>net.sf.jt400</groupId>
        <artifactId>jt400-full</artifactId>
        <version>7.10</version>
        <scope>compile</scope>
    </dependency>

    <!-- Testing Dependencies -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.jayway.restassured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>2.3.4</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.thetransactioncompany</groupId>
        <artifactId>cors-filter</artifactId>
        <version>2.2.1</version>
    </dependency>

</dependencies>

正在测试的方法:

public class CCResource {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Context
    HttpServletRequest request;

    @Autowired
    ServerConfig serverConfig;

    @GET
    @Path("/service")
    @Produces(MediaType.APPLICATION_JSON)
    public Object service() {

        return createResponse(new StringResult("Service: Credit Card"));
    }
}

测试Class:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration
public class QuickTest
{
    @Autowired
    CCResource ccService;

    @Test
    public void doATest()
    {
        OutboundJaxrsResponse resp =    (OutboundJaxrsResponse)ccService.service();
        assertTrue("Testing stuffs", 200 == resp.getStatus());
    }
}

堆栈跟踪:

org.junit.Assert.assertEquals(JJ)V
java.lang.NoSuchMethodError
    at org.junit.runners.model.TestClass.getOnlyConstructor(TestClass.java:204)
    at org.junit.runners.BlockJUnit4ClassRunner.validateZeroArgConstructor(BlockJUnit4ClassRunner.java:167)
    at org.junit.runners.BlockJUnit4ClassRunner.validateConstructor(BlockJUnit4ClassRunner.java:148)
    at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:127)
    at org.junit.runners.ParentRunner.validate(ParentRunner.java:416)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:104)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.junit.internal.requests.ClassRequest.buildRunner(ClassRequest.java:33)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:28)
    at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:51)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
    at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
    at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)   

您的 class 路径上有两个提供相同 class 的 JAR:org.junit.Assert.

其中一个是您所期望的:来自 junit:junit 的一个,另一个(也是 classloader 挑选的那个)来自 org.glassfish.distributions:web-all .

作为解决方案,您可能希望选择 latest version 的 web-all,它不再包括 org.junit 一堆 classes。

在这种情况下 duplicate-finder Maven 插件会有所帮助。