如何在@AfterClass中回滚数据库更改?

3
我知道如何配置Spring/JUnit以在每个测试用例之后回滚。我想要的是一种方法,可以启动并回滚所有测试用例的一个事务。
我使用@BeforeClass为我的HSQL DB准备了几个测试用例。然后,在@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中执行回滚的最佳方法的想法?

感谢所有人..

4个回答

5

如果您可以接受在每个测试后回滚并使用Spring,那么我的项目中的以下代码片段可能会对您有所帮助:

@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,就像您在这里一样。我想要的是让所有的单元测试用例都使用相同的数据库设置。这就是为什么我只需要进行一次设置,然后让每个单元测试都在同一个设置上运行。 - DhafirNz

3

你可能不希望在课程结束后进行回滚,而是在每个测试后进行回滚。测试不能保证每次以相同的顺序运行,因此可能会得到不同的结果。单元测试应该是隔离的。

您可以使用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
@DhafirNz,在阅读了你对Dmitriy的评论后,关于想要为所有单元测试使用相同的数据库,为什么不在运行任何测试之前先设置它呢?这是你想做的吗? - Jeff Storey
1
是的,我可以那样做,我们通常就是这样做的,但在这种特殊情况下,设置需要相当长的时间来完成,对于每个测试用例都运行它会非常慢。 - DhafirNz
为什么不在预测试设置中作为一部分进行一次,例如作为Maven目标或您正在使用的任何构建系统? - Jeff Storey
但这样行不通,对吧?因为测试数据并不适用于所有测试类,并且不能保证我的测试类会首先启动。其他集成测试可能会先启动,它们将无论如何清除数据库。此外,我使用DAO层在Java中以编程方式插入测试数据(约2000条记录),以控制组合方式,使得测试数据与测试用例位于同一位置。 - DhafirNz
抱歉,我误解了...你是对的,那样做行不通。由于数据库设置需要很长时间,我肯定会考虑使用dbunit进行存根处理。 - Jeff Storey

2

我成功地使用Spring/JUnit(而不是使用DBUnit)解决了这个问题。

简而言之,在@BeforeClass中调用transactionManager.getTransaction(def).setRollbackOnly();即可解决该问题。

Let me first explain what I was trying to do. My main motive was around this flow:

1. Start transaction
2. Insert load test data
3. run several test cases on the same test data
4. rollback test data.

Since I am building my load test data in @BeforeClass, I was looking to rollback in @AfterClass. This seems to be unnecessary as I can simply instruct the transaction to be rollback only in my @BeforeClass!

So here is how I did it:

@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.
  ...
}

This helped to rollback at the end of the class.

P.S. The SpecialDependencyInjectionTestExcecutionListener is an extension to DependencyInjectionTestExecutionListener which I used to override beforeTestClass to force the application.context to to be loaded before calling @BeforeClass. Credit goes to Dmitriy in highlighting this Listener which was the hint to solve another problem which i had in my mind.

Thanks to everyone who helped in highlighting and suggestions which collectively led me to this solution.

Dhafir


0

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

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

谢谢你的建议,Jeanne。我会研究一下DBUnit。我想我是在寻找最简单的方法来实现整个测试类的单个回滚,因为我们同时使用Spring进行配置测试。 - DhafirNz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接