如何在每次单元测试后重置数据库状态而不将整个测试作为事务处理?

Dav*_*ave 23 junit spring transactions hsqldb

我使用的是Spring 3.1.1.RELEASE,Hibernate 4.1.0.Final,JPA 2,JUnit 4.8.1和HSQL 2.2.7.我想在我的服务方法上运行一些JUnit测试,并且在每次测试之后,我希望回滚写入内存数据库的任何数据.但是,我不希望将整个测试视为事务.例如在这个测试中

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:test-context.xml" })
public class ContractServiceTest 
{
    …

    @Autowired
    private ContractService m_contractService;

    @Test
    public void testUpdateContract()
    {
        // Add the contract
        m_contractService.save(m_contract);
        Assert.assertNotNull(m_contract.getId());
        // Update the activation date by 6 months.
        final Calendar activationDate = Calendar.getInstance();
        activationDate.setTime(activationDate.getTime());
        activationDate.add(Calendar.MONTH, 6);
        m_contract.setActivationDate(activationDate.getTime());
        m_contractService.save(m_contract);
        final List<Contract> foundContracts = m_contractService.findContractByOppId(m_contract.getOpportunityId());
        Assert.assertEquals(foundContracts.get(0), m_contract);
    }   // testUpdateContract
Run Code Online (Sandbox Code Playgroud)

有三个服务调用("m_contractService.save","m_contractService.save"和"m_contractService.findContractByOppId"),每个调用都被视为一个事务,我想要它.但我不知道在每次单元测试后如何将内存数据库重置为其原始状态.

如果我需要提供其他信息,请与我们联系.

Sol*_*ris 20

由于您正在使用hibernate,因此您可以使用属性hibernate.hbm2ddl.auto每次启动时创建数据库.您还需要在每次测试后强制重新加载spring上下文.您可以使用@DirtiesContext注释执行此操作.

这可能会给您的测试增加额外的开销,因此另一种解决方案是只手动删除每个表中的数据.

  • 我在课程级别添加了"@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)",它完成了我需要的工作.有额外的开销,但在我的情况下并不重要.摇滚 (5认同)

Sim*_*wig 8

@DirtiesContext对我来说没有解决方案,因为整个应用程序上下文都被破坏了,必须在每次测试后创建 - >花了很长时间.

@Before对我来说也不是一个好的解决方案,因为我必须在每个IntegrationTest中创建@Before

所以我决定创建一个TestExecutionListener,它在每次测试后重新创建数据库.(使用liquibase,但它也适用于flyway和普通的sql)

public class CleanupDatabaseTestExecutionListener
extends AbstractTestExecutionListener {

public final int getOrder() {
    return 2001;
}

private boolean alreadyCleared = false;

@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
    if (!alreadyCleared) {
        cleanupDatabase(testContext);
        alreadyCleared = true;
    } else {
        alreadyCleared = true;
    }
}

@Override
public void afterTestClass(TestContext testContext) throws Exception {
    cleanupDatabase(testContext);
}

private void cleanupDatabase(TestContext testContext) throws LiquibaseException {
    ApplicationContext app = testContext.getApplicationContext();
    SpringLiquibase springLiquibase = app.getBean(SpringLiquibase.class);
    springLiquibase.setDropFirst(true);
    springLiquibase.afterPropertiesSet(); //The database get recreated here
}
}
Run Code Online (Sandbox Code Playgroud)

要使用TestExecutionListenere,我创建了一个自定义测试注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = OurderApp.class)
@TestExecutionListeners(mergeMode = 
TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
    listeners = {CleanupDatabaseTestExecutionListener.class}
)
public @interface OurderTest {
}
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要的我现在可以创建测试,我可以确定数据库处于干净模式.

@RunWith(SpringRunner.class)
@OurderTest
public class ProductSaveServiceIntTest {
 }
Run Code Online (Sandbox Code Playgroud)

编辑:我改进了我的解决方案.我遇到的问题是,有时一个测试方法会破坏我的数据库,以便测试类中所有即将进行的测试.所以我创建了注释

package com.ourder.e2e.utils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClearContext {
}
Run Code Online (Sandbox Code Playgroud)

并将其添加到CleanupDatabaseTestExectionListener.

@Override
public void afterTestMethod(TestContext testContext) throws Exception {
    if(testContext.getTestMethod().getAnnotation(ClearContext.class)!=null){
        cleanupDatabase(testContext);
    }
    super.afterTestMethod(testContext);
}
Run Code Online (Sandbox Code Playgroud)

在这两个片段的帮助下,我现在可以创建这样的测试:

@Test
@ClearContext
public void testWhichDirtiesDatabase() {}
Run Code Online (Sandbox Code Playgroud)