繁体   English   中英

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

[英]Disabling transaction on spring testng test method

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

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

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

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

恕我直言,这里的 Spring 方法非常不灵活。 但是有一种解决方法可以解决您的问题。 在 Spring TransactionTemplate类中封装所需的逻辑就足够了。 这将确保您的测试用例中的代码将在新事务中启动。


个人建议:从我的角度来看,最好和最灵活的方法是从一开始就放弃@Transactional测试,并在每次测试之前将数据库设置为已知状态。 这样,Spring 将以与生产环境完全相同的方式管理事务。
没有怪癖,没有黑客,没有手动交易管理。

我知道将@Transactional与围绕单元测试的“回滚”策略一起使用是一个诱人的想法,但它有太多的陷阱。 我推荐阅读这篇文章Spring Pitfalls: Transactional tests considered dangerous

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

我发现通过在单独的线程中执行我的测试主体可以防止 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; 
    } 
 } 
}

Spring Boot >=2.1.5.RELEASE 有以下解决方案。

默认情况下,数据 JPA 测试是事务性的,并在每个测试结束时回滚。 有关更多详细信息,请参阅 Spring Framework 参考文档中的相关部分。 如果这不是您想要的,您可以为测试或整个班级禁用事务管理,如下所示:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringRunner.class)
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {

}

(s. Spring Doc )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM