在spring testng测试方法上禁用事务

时间:2015-01-16 15:17:49

标签: java spring jpa transactions testng

我正在使用TestNG和Spring以及JPA。到目前为止,我正在使用测试数据库填充我的测试类AbstractTransactionalTestNGSpringContextTests。使用@TransactionConfiguration(defaultRollback = true)一切正常,我不需要担心清理。 Spring在我的每个测试方法的开头创建一个默认事务,而不是回滚。这是一个非常巧妙的技巧来解决着名的"交易测试被认为是有害的"问题。

不幸的是,我需要这个类中的一个方法(一个测试)没有这个默认事务。这是因为这个测试方法模拟了批处理,我在其中有多个与生产无关的事务。我能够模拟和解决问题的唯一方法是使用Propagation.REQUIRES_NEW配置这些内部事务,但我不希望在生产代码中使用。有没有办法为我的特定测试方法禁用Spring事务(所以我不需要在我的服务方法中使用Propagation.REQUIRES_NEWPropagation.REQUIRED)?

2 个答案:

答案 0 :(得分:3)

我知道你已经解决了你的问题,但对于那些将来会来这里的人来说......

不幸的是,似乎没有办法禁用使用@Transactional注释的测试中的现有事务。

这里的IMHO Spring方法极不灵活。但是你的问题有一个解决方法。将所需的逻辑封装在Spring TransactionTemplate类中就足够了。这将确保测试用例中的代码将在新事务中启动。

个人建议:从我的角度来看,最好也是最灵活的方法是从最初@Transactional测试和设置数据库放弃到已知状态之前每一次测试。通过这种方式,Spring将以与生产中完全相同的方式管理交易 没有怪癖,没有黑客,也没有人工交易管理。

我知道在单元测试中使用@Transactional和“回滚”策略是一个诱人的想法,但它有太多陷阱。我建议您阅读这篇文章Spring Pitfalls: Transactional tests considered harmful

当然,我不会在这里抱怨@Transactional本身 - 因为它极大地简化了生产代码中的事务管理。

答案 1 :(得分:1)

我发现通过在单独的线程中执行我的测试体来阻止Spring进行事务处理。因此,解决方案解决方案类似于:

    @ContextConfiguration(classes = { test.SpringTestConfigurator.class })
    @TransactionConfiguration(defaultRollback = false)
    @Slf4j
    @WebAppConfiguration
    public class DBDataTest extends AbstractTransactionalTestNGSpringContextTests {    
    /**
     * Variable to determine if some running thread has failed.
     */
    private volatile Exception threadException = null;

   @Test(enabled = true)
    public void myTest() {
        try {
            this.threadException = null;
            Runnable task = () -> {
                myTestBody();
            };
            ExecutorService executor = Executors.newFixedThreadPool(1);
            executor.submit(task);
            executor.shutdown();
            while (!executor.isTerminated()) {
                if (this.threadException != null) {
                    throw this.threadException;
                }
            }
            if (this.threadException != null) {
                throw this.threadException;
            }
        } catch (Exception e) {
            log.error("Test has failed.", e);
            Assert.fail();
        }
    }

 public void myTestBody() {
    try {
        // test body to do
    }
    catch (Exception e) {
       this.threadException = e; 
    } 
 } 
}