【问题标题】:How to rollback DB changes in @AfterClass?如何回滚@AfterClass 中的数据库更改?
【发布时间】:2012-07-08 22:39:10
【问题描述】:

我知道如何配置 Spring/JUnit 以在每个测试用例后回滚。 我所追求的是一种为所有测试用例启动和回滚一个事务的方法。

我正在使用@BeforeClass 为几个测试用例准备我的 HSQL 数据库。然后我想在@AfterClass 中的所有测试用例结束后回滚更改。

实现此回滚的最佳方法是什么?

这是我的代码示例:

@BeforeClass
public static void setupDB(){
ApplicationContext context = new ClassPathXmlApplicationContext(
        "classpath:/spring/applicationContext-services-test.xml");
//- get beans and insert some records to the DB
...
}

@AfterClass
public static void cleanUp(){
   ??? what should go here?
}

关于在 AfterClass 中进行回滚的最佳方法有什么想法吗?

谢谢大家..

【问题讨论】:

    标签: java junit rollback


    【解决方案1】:

    如果您可以在每次测试后回滚并使用 Spring,我项目中的以下 sn-p 可能会对您有所帮助:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "classpath:/net/sukharevd/shopzilla/model/application-context-dao.xml" })
    @TestExecutionListeners(DependencyInjectionTestExecutionListener.class)
    @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
    @Transactional
    public class HibernateCategoryDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
    

    【讨论】:

    • 感谢您的回复。我将 SpringJUnit4ClassRunner 用于我的其他单元测试类,就像你在这里一样。我想要的是我的所有单元测试用例都使用相同的数据库设置。这就是为什么我只做一次设置,让每个单元测试都在相同的设置上工作。
    【解决方案2】:

    您可能不想在课后进行回滚,而是在每次测试之后进行。不能保证每次测试都以相同的顺序运行,因此您可能会得到不同的结果。单元测试应该是独立的。

    您可以使用 spring 测试运行器来注释您的类和回滚事务

    @RunWith(SpringJUnit4ClassRunner.class)
    @TransactionConfiguration(defaultRollback=true)
    public static class YourTestClass {
    
        @Test
        @Transactional
        public void aTest() {
            // do some db stuff that will get rolled back at the end of the test
        }
    
    }
    

    一般来说,尽管您也应该尽量避免在单元测试中碰到真实的数据库。命中数据库的测试通常是集成级别的测试(或者甚至更粗粒度的测试,如验收测试)。 DBUnit http://www.dbunit.org/ 框架用于为单元测试存根数据库,因此您不需要使用真正的数据库。

    【讨论】:

    • 感谢您的回复。是的,我同意你所说的,并且我将 SpringJUnit4ClassRunner 用于我的其他测试用例。然而,这是一种特殊情况,我必须使用适用于所有测试用例的大量记录来设置数据库。在每个测试用例(@Before)之前重新运行设置很乏味,并且会减慢单元测试的速度。我也没有使用真正的数据库,它是一个临时的 HSQL 文件基础测试数据库,但我不想设置影响其他测试类,所以我希望能够在完成后回滚我的设置。
    • @DhafirNz,在阅读了您对 Dmitriy 关于希望对所有单元测试使用相同数据库的评论之后,为什么不在运行任何测试之前设置一次呢?这就是你想要做的吗?
    • 是的,我可以这样做,我们通常会这样做,但在这种特殊情况下,设置需要相当长的时间才能完成,为每个测试用例运行它会非常慢。
    • 为什么不做一次作为预测试设置的一部分,例如 maven 目标或您正在使用的任何构建系统
    • 但这行不通,不是吗?因为测试数据不是针对所有测试类的,也不能保证我的测试类会首先启动。其他集成测试可以先开始,无论如何它们都会清除数据库。此外,我使用 DAO 层(大约 2000 条记录)以受控组合在 Java 中以编程方式插入测试数据,因此测试数据与测试用例位于同一位置。
    【解决方案3】:

    我设法仅使用 Spring/JUnit(不使用 DBUnit)解决了这个问题。 简而言之,解决方案是在@BeforeClass 中调用transactionManager.getTransaction(def).setRollbackOnly();

    让我先解释一下我想要做什么。 我的主要动机是围绕这个流程:

    1. 开始交易
    2. 插入负载测试数据
    3. 在相同的测试数据上运行多个测试用例
    4.回滚测试数据。

    由于我在@BeforeClass 中构建负载测试数据,因此我希望在@AfterClass 中回滚。这似乎是不必要的,因为我可以简单地指示事务仅在我的@BeforeClass 中回滚!

    这就是我的做法:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:/spring/applicationContext-services-test.xml")
    @TestExecutionListeners(inheritListeners = false, listeners = { SpecialDependencyInjectionTestExcecutionListener.class })
    @TransactionConfiguration(defaultRollback = true)
    @Transactional
    public class loadTest {
    ...
    private static HibernateTransactionManager transactionManager;
    ...
    
    @BeforeClass
    public static void setupDB() {
      //- set the transaction to rollback only. We have to get a new transaction for that.
      DefaultTransactionDefinition def = new DefaultTransactionDefinition();
      def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
      transactionManager.getTransaction(def).setRollbackOnly();
      ...
      //- start loading the data using the injected services.
      ...
    }
    

    这有助于在课程结束时回滚。

    附言SpecialDependencyInjectionTestExcecutionListenerDependencyInjectionTestExecutionListener 的扩展,我曾经覆盖 beforeTestClass 以强制在调用 @BeforeClass 之前加载 application.context。归功于 Dmitriy 突出显示了这个 Listener,这是解决我脑海中另一个问题的提示。

    感谢所有帮助突出显示和建议的人,这些人共同引导我找到了这个解决方案。

    达菲尔

    【讨论】:

      【解决方案4】:

      让我们假设您没有使用 Spring。 (因为使用 Spring,这里的其他答案更好。)那么您将有三个选择:

      1. 使用dbunit 为您处理数据加载/清理。 (该网站目前已关闭,但如果您使用 Google 搜索,您可以看到一些教程。)
      2. 为手动更新创建手动删除
      3. 在您的数据库中创建一个回滚点作为设置的第一步。这是how to do so in Oracle.

      【讨论】:

      • 感谢珍妮的建议,我会研究 DBUnit。我想我正在寻找实现整个测试类单次回滚的最简单方法,因为我们使用的是 Spring 9,我们必须同时测试配置。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-10
      • 1970-01-01
      • 1970-01-01
      • 2016-02-10
      • 1970-01-01
      • 2017-11-23
      • 1970-01-01
      相关资源
      最近更新 更多