JUnit 测试在 Eclipse 中通过,但在 MavenSurefire 中失败

我使用 JUnit4和 spring-test 库编写了一些 JUnit 测试。当我在 Eclipse 中运行测试时,运行良好并通过。但是当我使用 Maven (在构建过程中)运行它们时,它们会出现一个与弹簧相关的错误。我不确定是什么导致了这个问题,JUnit、 Surefire 还是 Spring。下面是我的测试代码、 Spring 配置和我从 Maven 得到的异常:

Java

package com.xyz.person.test;


import static com.xyz.person.util.FjUtil.toFjList;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;


import java.util.List;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;


import com.xyz.person.bo.Person;
import com.xyz.person.bs.PersonService;


import fj.Effect;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:personservice-test.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
public class PersonServiceTest {


@Autowired
private PersonService service;


@Test
@Transactional
public void testCreatePerson() {
Person person = new Person();
person.setName("abhinav");
service.createPerson(person);


assertNotNull(person.getId());
}


@Test
@Transactional
public void testFindPersons() {
Person person = new Person();
person.setName("abhinav");
service.createPerson(person);


List<Person> persons = service.findPersons("abhinav");
toFjList(persons).foreach(new Effect<Person>() {
public void e(final Person p) {
assertEquals("abhinav", p.getName());
}});
}


}

Personservice-test. xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">


<import resource="classpath:/personservice.xml" />


<bean id="datasource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
lazy-init="true">
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="url" value="jdbc:derby:InMemoryDatabase;create=true" />
</bean>


<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="datasource" />
<property name="persistenceUnitName" value="PersonService" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.DerbyDialect" />
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.validator.autoregister_listeners" value="false" />
<entry key="javax.persistence.transactionType" value="RESOURCE_LOCAL" />
</map>
</property>
</bean>


<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="dataSource" ref="datasource" />
</bean>


<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="false" />


<bean id="beanMapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>personservice-mappings.xml</value>
</list>
</property>
</bean>


</beans>

玛文的例外

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.xyz.person.test.PersonServiceTest
23:18:51,250  WARN JDBCExceptionReporter:77 - SQL Warning: 10000, SQLState: 01J01
23:18:51,281  WARN JDBCExceptionReporter:78 - Database 'InMemoryDatabase' not created, connection made to existing database instead.
23:18:52,937  WARN JDBCExceptionReporter:77 - SQL Warning: 10000, SQLState: 01J01
23:18:52,937  WARN JDBCExceptionReporter:78 - Database 'InMemoryDatabase' not created, connection made to existing database instead.
23:18:52,953  WARN TestContextManager:429 - Caught exception while allowing TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@359a359a] to process 'after' execution for test: method [public void com.xyz.person.test.PersonServiceTest.testCreatePerson()], instance [com.xyz.person.test.PersonServiceTest@1bc81bc8], exception [org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! JpaTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single JpaTransactionManager for all transactions on a single DataSource, no matter whether JPA or JDBC access.]
java.lang.IllegalStateException: No value for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@3f563f56] bound to thread [main]
at org.springframework.transaction.support.TransactionSynchronizationManager.unbindResource(TransactionSynchronizationManager.java:199)
at org.springframework.orm.jpa.JpaTransactionManager.doCleanupAfterCompletion(JpaTransactionManager.java:489)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:1011)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:804)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:515)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:290)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:183)
at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:426)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:90)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:59)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:115)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:102)
at org.apache.maven.surefire.Surefire.run(Surefire.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:599)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:350)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1021)
23:18:53,078  WARN TestContextManager:377 - Caught exception while allowing TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@359a359a] to process 'before' execution of test method [public void com.xyz.person.test.PersonServiceTest.testFindPersons()] for test instance [com.xyz.person.test.PersonServiceTest@79f279f2]
org.springframework.transaction.IllegalTransactionStateException: Pre-bound JDBC Connection found! JpaTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single JpaTransactionManager for all transactions on a single DataSource, no matter whether JPA or JDBC access.
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:304)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.startTransaction(TransactionalTestExecutionListener.java:507)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.startNewTransaction(TransactionalTestExecutionListener.java:269)
at org.springframework.test.context.transaction.TransactionalTestExecutionListener.beforeTestMethod(TransactionalTestExecutionListener.java:162)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:374)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:240)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:59)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:115)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:102)
at org.apache.maven.surefire.Surefire.run(Surefire.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:599)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:350)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1021)
Tests run: 3, Failures: 0, Errors: 3, Skipped: 0, Time elapsed: 15.625 sec <<< FAILURE!


Results :


Tests in error:
testCreatePerson(com.xyz.person.test.PersonServiceTest)
testCreatePerson(com.xyz.person.test.PersonServiceTest)
testFindPersons(com.xyz.person.test.PersonServiceTest)


Tests run: 3, Failures: 0, Errors: 3, Skipped: 0
129979 次浏览

您不需要在 JpaTransactionManager 中注入 DataSource,因为 EntityManagerFactory 已经有了一个数据源:

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

通常,当测试在 eclipse 中通过而在 maven 中失败时,这是一个类路径问题,因为它是两者之间的主要区别。

因此,您可以使用 maven-X 测试检查类路径,并通过菜单或在。类路径文件。

例如,您确定 Personservice-test.xml 在类路径中吗?

我也有类似的问题,但是使用 IntelliJIDEA + Maven + TestNG + spring-test。(弹簧试验当然是必不可少的:) 当我改变 Maven-surefire-plugin的配置以禁用并行运行测试时,它就被修复了。像这样:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<skipTests>${maven.test.skip}</skipTests>
<trimStackTrace>false</trimStackTrace>
<!--<parallel>methods</parallel>-->
<!-- to skip integration tests -->
<excludes>
<exclude>**/IT*Test.java</exclude>
<exclude>**/integration/*Test.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skipTests>${maven.integration-test.skip}</skipTests>
<!-- Make sure to include this part, since otherwise it is excluding Integration tests -->
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/IT*Test.java</include>
<include>**/integration/*Test.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>

我突然遇到了这个错误,我的解决方案是禁用并行运行测试。

您的收获可能会有所不同,因为我可以通过将 surefire 配置为按类运行并行测试来降低失败测试的数量。 :

            <plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<parallel>classes</parallel>
<threadCount>10</threadCount>
</configuration>
</plugin>

正如我首先写的,这对我的测试套件来说还不够,所以我通过删除 <configuration>部分完全禁用了并行。

这并不完全适用于您的情况,但是我遇到了同样的情况——当 Maven 的测试目标运行时,在 Eclipse 中通过的测试失败了。

It turned out to be a test earlier in my suite, 在另一个包裹里. This took me a week to solve!

早期的测试是测试一些 Logback 类,并从配置文件创建一个 Logback 上下文。

后面的测试是测试 Spring 的 SimpleRestTemplate 的一个子类,并且以某种方式保存了早期的 Logback 上下文,DEBUG 处于打开状态。这导致在 RestTemplate 中发出额外的调用来记录 HttpStatus,等等。

It's another thing to check if one ever gets into this situation. I fixed my problem by injecting some Mocks into my Logback test class, so that no real Logback contexts were created.

我也遇到过类似的问题,测试代码中的注释 @Autowired在使用 Maven 命令行时无法正常工作,而在 Eclipse 中却可以正常工作。我刚刚把我的 JUnit 版本从4.4升级到4.9,问题就解决了。

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>

今天我在测试一个将包含 Map的对象转换为 JSON 字符串的方法时遇到了这个问题。我假设 Eclipse 和 Maven surefire 插件使用了不同的 JRE,这些 JRE 有不同的 HashMap排序实现之类的,这导致通过 Eclipse 运行的测试通过,通过 surefire 运行的测试失败(assertEquals失败)。最简单的解决方案是使用具有可靠排序的 Map 实现。

我遇到了同样的问题(JUnit 测试在 Maven Suurefire 中失败,但在 Eclipse 中通过了) ,并设法通过在 pom.xml 中的 Maven Surefire 配置中将 ForkMode设置为 一直都是来解决这个问题:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<configuration>
<forkMode>always</forkMode>
</configuration>
</plugin>

确保发射参数: http://maven.apache.org/plugins/maven-surefire-plugin/test-mojo.html

编辑(2014年1月) :

正如 彼得,也许所指出的,自从 Surefire 2.14以来,forkMode 参数已被弃用。从 Surefire 2.14开始,使用以下代替:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<reuseForks>false</reuseForks>
<forkCount>1</forkCount>
</configuration>
</plugin>

For more information see 分叉选项和并行测试执行

[我不确定这是否就是最初问题的答案,因为这里的堆栈跟踪看起来略有不同,但它可能对其他人有用。]

当您还在运行 Cobertura 时,您可以在 Surefire 中得到失败的测试(以获得代码覆盖率报告)。这是因为 Cobertura 需要代理(用于度量代码使用) ,并且这些代理和 Spring 代理之间存在某种冲突。当 Spring 使用 cglib2时发生此 only,例如,如果有 proxy-target-class="true",或者如果有一个正在被代理的对象没有实现接口,就会发生这种情况。

通常的修复方法是添加一个接口。因此,例如,DAO 应该是由 DAOImpl 类实现的接口。如果在接口上自动连接,那么一切都会正常工作(因为不再需要 cglib2; 可以使用接口的更简单的 JDK 代理,Cobertura 可以很好地使用它)。

但是,你不能使用带注释控制器的接口(在 servlet 中尝试使用控制器时会出现运行时错误)-我没有一个 Cobertura + Spring 自动装配控制器测试的解决方案。

配置文件很可能在 Src/main/resources中,而它们必须在 Src/test/resources下才能在 maven 下正常工作。

Https://cwiki.apache.org/uima/differences-between-running-unit-tests-in-eclipse-and-in-maven.html

我在两年后回答这个问题,因为我在这里找不到答案,我认为这是正确的答案。

我遇到了同样的问题,但是对我来说问题是 Java 断言(例如断言(num > 0))没有为 Eclipse 启用,而是在运行 maven 时启用了。

因此,从 Eclipse 运行 jUnit 测试不会触发断言错误。

This is made clear when using jUnit 4.11 (as opposed to the older version I was using) because it prints out the assertion error, e.g.

java.lang.AssertionError: null
at com.company.sdk.components.schema.views.impl.InputViewHandler.<init>(InputViewHandler.java:26)
at test.com.company.sdk.util.TestSchemaExtractor$MockInputViewHandler.<init>(TestSchemaExtractor.java:31)
at test.com.company.sdk.util.TestSchemaExtractor.testCreateViewToFieldsMap(TestSchemaExtractor.java:48)

这帮助我解决了我的问题。我有一个类似的症状,即 maven 会失败,但是运行 junit 测试运行良好。

As it turns out my parent pom.xml contains the following definition:

    <plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<forkMode>pertest</forkMode>
<argLine>-Xverify:none</argLine>
</configuration>
</plugin>

在我的项目中,我重写它来删除 argLine:

    <plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>pertest</forkMode>
<argLine combine.self="override"></argLine>
</configuration>
</plugin>

希望这将帮助有人在故障排除肯定火插件。

我也遇到过类似的问题: 当我使用 SpringSource Bundle Repository 中的 JUnit 库版本4.11.0时,JUnit 测试在 Maven Surefire 中失败,但在 Eclipse 中通过。特别:

<dependency>
<groupId>org.junit</groupId>
<artifactId>com.springsource.org.junit</artifactId>
<version>4.11.0</version>
</dependency>

然后我用下面的 JUnit 库4.11版本替换了它,一切正常。

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>

I had the same problem, and the solution for me was to allow Maven to handle all dependencies, including to local jars. I used Maven for online dependencies, and configured build path manually for local dependencies. 因此,Maven 不知道我手动配置的依赖项。

I used this solution to install the local jar dependencies into Maven:

How to add local jar files in maven project?

I had a similar problem with a different cause and therefore different solution. In my case, I actually had an error where a singleton object was having a member variable modified in a non-threadsafe way. In this case, following the accepted answers and circumventing the parallel testing would only hide the error that was actually revealed by the test. My solution, of course, is to fix the design so that I don't have this bad behavior in my code.

测试执行结果不同于 JUnit runmaven install,似乎是一些问题的症状。

在我们的例子中,禁用线程重用测试执行也消除了症状,但是代码不是线程安全的印象仍然很强烈。

在我们的例子中,差异是由于存在修改测试行为的 bean。只运行 JUnit 测试会很好,但是运行项目 install目标会导致测试用例失败。因为它是正在开发的测试用例,所以立即引起了怀疑。

它导致了另一个测试用例通过 Spring 实例化一个 bean,这个 bean 将一直存在到新测试用例的执行。Bean 的存在修改了一些类的行为并产生了失败的结果。

The solution in our case was getting rid of the bean, which was not needed in the first place (yet another prize from the 复制 + 粘贴 gun).

我建议每个有这种症状的人调查一下根本原因是什么。在测试执行中禁用线程重用可能只会隐藏它。

在我的例子中,原因是代码中的一个 bug。这个测试依赖于 HashSet中某个元素的顺序,在 Eclipse 或 MavenSurefire 中运行时,结果是不同的。

我也遇到过类似的问题,我运行了我的测试,禁用了像这样的分叉的重用

mvn clean test -DreuseForks=false

然后问题就消失了。 缺点是整个测试执行时间将会更长,这就是为什么您可能只在必要时才希望从命令行执行此操作