maven-surefire-plugin 运行单一方法,但在 class 上失败
maven-surefire-plugin runs single method, but failed on class
我写了需要交易的测试,它看起来像:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ExchangeApp.class)
@EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY)
@ActiveProfiles({JHipsterConstants.SPRING_PROFILE_TEST})
public abstract class AbstractServiceTest {
所以当我运行单测方法:mvn test -Dtest=TestClassName#method1
按预期工作,但
mvn test -Dtest=TestClassName
失败,有奇怪的异常,异常表示@OneToMany 中的约束违反以及 BigDecimal 中除法期间的小数计算异常。当我在 IDE 中 运行 时出现相同的异常。
看起来事务管理被遗漏了。有什么想法吗?
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FK_OPEN_EXEC_ID: PUBLIC.ORDER_PAIR_OPEN_EXEC FOREIGN KEY(EXECUTIONS_ID) REFERENCES PUBLIC.ORDER_PAIR_OPEN(ID) (2)"; SQL statement:
delete from order_pair_open where id=? [23503-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
更新:我也已经试过了
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
<parallel>classes</parallel>
<threadCountClasses>1</threadCountClasses>
<threadCountMethods>1</threadCountMethods>
<threadCountSuites>1</threadCountSuites>
</configuration>
</plugin>
UPD:这是针对我的情况。我正在尝试使用内部的 @Async
方法测试服务,所以似乎我必须在测试方法名称上标记 @Transactional
,以便启用事务支持,这就是我尝试使用 @EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY)
的原因在 class 测试期间启用事务管理。这是伪代码:
class Service1Test extends AbstractServiceTest {
Service1 service1;
Repo1 repo1;
//Does not works with class call, but works with method call
//when I mark this method with @Transactional, mentioned exceptions are gone,
// but I cant check result since "registerSynchronization" were not called
@Test
public void test1() throws InterruptedException {
service1.method1();
synchronized (this) {
wait(2000l);
}
assertThat( repo1.findAll().size()).isEqualTo(1);
//repoN check
}
}
@Service
@Transactional
class Service1 {
Service2 service2;
@Async
public void method1() {
//DB operations...
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
service2.method2();
}
});
}
}
@Service
class Service2 {
Repo1 repo1;
public void method2() {
repo1.save(new Entity1());
}
}
@Service
class Service3 {
@Autowired
private ScheduledExecutorService scheduler;
public void method3() {
scheduler.schedule(() -> {
//other transactional services call
}, 1l, TimeUnit.SECONDS);
}
}
@Repository
interface Repo1 extends JpaRepository<Entity1, Long> {
}
@Entity
class Entity1{
}
我无法在 JHipster 方面发表评论,但一个可能的原因是 "Test Transactional" 行为未应用于测试代码。
澄清一下,默认情况下,如果您作为@Transactional 进行测试,spring 打开一个事务,测试 运行s,以及它何时完成(无论它是通过还是失败) 事务执行回滚有效清理数据库。
现在,我在测试中没有看到这一点,所以您可能不会使用这种行为。
但这是纯粹的 spring,而不是 spring 引导。
现在关于 spring 启动部分。
如果您将 @SpringBootTest
与具体配置一起使用 ExchangeApp
在这种情况下,它很可能不会加载任何自动配置(例如那些定义与事务一起使用的配置,数据源管理等)。
如果你想"mimic"微服务的负载,你应该运行 @SpringBootTest
不配置但它超出了问题的范围。
"by the book" spring 使用休眠测试 DAO 的启动方式使用 @DataJpaTest
仅加载与数据库相关的内容,但不能与 [=10= 一起使用] - 你应该选择一个。
所以对我来说,很明显测试做了一些棘手的事情,绝对不是遵循 spring 引导约定的事情,所以可能 spring/spring 引导反击 :)
现在,关于异步的东西。这也可能导致混乱,因为 spring 中的事务支持在很大程度上依赖于线程本地概念,因此当新线程被执行时(在另一个线程池或其他东西上),有关事务的信息不会传播,因此 spring 无法理解它仍在同一个事务中。我看到你使用了 TransactionSynchronizationManager
但没有调试很难说出会发生什么。
现在为了检查为什么交易没有传播,我认为你应该调试应用程序并查看:
- 服务是否包装在支持事务的代理中(这就是
@Transactional
所做的,假设应用了相关的 BeanPostProcessor)
- 检查您是否在交易的每个步骤中
- 考虑在测试/测试用例上使用@Transactional,这样它会清除在测试期间应用的更改
我写了需要交易的测试,它看起来像:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ExchangeApp.class)
@EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY)
@ActiveProfiles({JHipsterConstants.SPRING_PROFILE_TEST})
public abstract class AbstractServiceTest {
所以当我运行单测方法:mvn test -Dtest=TestClassName#method1
按预期工作,但
mvn test -Dtest=TestClassName
失败,有奇怪的异常,异常表示@OneToMany 中的约束违反以及 BigDecimal 中除法期间的小数计算异常。当我在 IDE 中 运行 时出现相同的异常。 看起来事务管理被遗漏了。有什么想法吗?
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FK_OPEN_EXEC_ID: PUBLIC.ORDER_PAIR_OPEN_EXEC FOREIGN KEY(EXECUTIONS_ID) REFERENCES PUBLIC.ORDER_PAIR_OPEN(ID) (2)"; SQL statement:
delete from order_pair_open where id=? [23503-197]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
更新:我也已经试过了
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
<parallel>classes</parallel>
<threadCountClasses>1</threadCountClasses>
<threadCountMethods>1</threadCountMethods>
<threadCountSuites>1</threadCountSuites>
</configuration>
</plugin>
UPD:这是针对我的情况。我正在尝试使用内部的 @Async
方法测试服务,所以似乎我必须在测试方法名称上标记 @Transactional
,以便启用事务支持,这就是我尝试使用 @EnableTransactionManagement(proxyTargetClass = true, mode = AdviceMode.PROXY)
的原因在 class 测试期间启用事务管理。这是伪代码:
class Service1Test extends AbstractServiceTest {
Service1 service1;
Repo1 repo1;
//Does not works with class call, but works with method call
//when I mark this method with @Transactional, mentioned exceptions are gone,
// but I cant check result since "registerSynchronization" were not called
@Test
public void test1() throws InterruptedException {
service1.method1();
synchronized (this) {
wait(2000l);
}
assertThat( repo1.findAll().size()).isEqualTo(1);
//repoN check
}
}
@Service
@Transactional
class Service1 {
Service2 service2;
@Async
public void method1() {
//DB operations...
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
service2.method2();
}
});
}
}
@Service
class Service2 {
Repo1 repo1;
public void method2() {
repo1.save(new Entity1());
}
}
@Service
class Service3 {
@Autowired
private ScheduledExecutorService scheduler;
public void method3() {
scheduler.schedule(() -> {
//other transactional services call
}, 1l, TimeUnit.SECONDS);
}
}
@Repository
interface Repo1 extends JpaRepository<Entity1, Long> {
}
@Entity
class Entity1{
}
我无法在 JHipster 方面发表评论,但一个可能的原因是 "Test Transactional" 行为未应用于测试代码。
澄清一下,默认情况下,如果您作为@Transactional 进行测试,spring 打开一个事务,测试 运行s,以及它何时完成(无论它是通过还是失败) 事务执行回滚有效清理数据库。
现在,我在测试中没有看到这一点,所以您可能不会使用这种行为。 但这是纯粹的 spring,而不是 spring 引导。
现在关于 spring 启动部分。
如果您将 @SpringBootTest
与具体配置一起使用 ExchangeApp
在这种情况下,它很可能不会加载任何自动配置(例如那些定义与事务一起使用的配置,数据源管理等)。
如果你想"mimic"微服务的负载,你应该运行 @SpringBootTest
不配置但它超出了问题的范围。
"by the book" spring 使用休眠测试 DAO 的启动方式使用 @DataJpaTest
仅加载与数据库相关的内容,但不能与 [=10= 一起使用] - 你应该选择一个。
所以对我来说,很明显测试做了一些棘手的事情,绝对不是遵循 spring 引导约定的事情,所以可能 spring/spring 引导反击 :)
现在,关于异步的东西。这也可能导致混乱,因为 spring 中的事务支持在很大程度上依赖于线程本地概念,因此当新线程被执行时(在另一个线程池或其他东西上),有关事务的信息不会传播,因此 spring 无法理解它仍在同一个事务中。我看到你使用了 TransactionSynchronizationManager
但没有调试很难说出会发生什么。
现在为了检查为什么交易没有传播,我认为你应该调试应用程序并查看:
- 服务是否包装在支持事务的代理中(这就是
@Transactional
所做的,假设应用了相关的 BeanPostProcessor) - 检查您是否在交易的每个步骤中
- 考虑在测试/测试用例上使用@Transactional,这样它会清除在测试期间应用的更改