복붙노트

[SPRING] 전체 테스트를 트랜잭션없이 각 단위 테스트 후에 데이터베이스 상태를 어떻게 재설정합니까?

SPRING

전체 테스트를 트랜잭션없이 각 단위 테스트 후에 데이터베이스 상태를 어떻게 재설정합니까?

저는 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

서비스 ( "m_contractService.save", "m_contractService.save"및 "m_contractService.findContractByOppId")에 대한 세 가지 호출이 있으며 각각은 원하는 트랜잭션으로 처리됩니다. 그러나 각 단위 테스트 후에 메모리 데이터베이스를 원래 상태로 재설정하는 방법을 모르겠습니다.

추가 정보를 제공해야하는지 알려주세요.

해결법

  1. ==============================

    1.당신이 최대 절전 모드를 사용하고 있기 때문에, hibernate.hbm2ddl.auto 프로퍼티를 사용하여 매번 시작할 때 데이터베이스를 생성 할 수있다. 또한 각 테스트 후에 스프링 컨텍스트를 강제로 다시로드해야합니다. @DirtiesContext 주석으로이를 수행 할 수 있습니다.

    당신이 최대 절전 모드를 사용하고 있기 때문에, hibernate.hbm2ddl.auto 프로퍼티를 사용하여 매번 시작할 때 데이터베이스를 생성 할 수있다. 또한 각 테스트 후에 스프링 컨텍스트를 강제로 다시로드해야합니다. @DirtiesContext 주석으로이를 수행 할 수 있습니다.

    이렇게하면 테스트에 약간의 오버 헤드가 추가 될 수 있으므로 다른 해결책은 각 테이블에서 수동으로 데이터를 삭제하는 것입니다.

  2. ==============================

    2.@DirtiesContext는 나를위한 해결책이 아니 었습니다. 왜냐하면 전체 응용 프로그램 컨텍스트가 파괴되기 때문에 각 테스트 후에 만들어야하기 때문입니다.

    @DirtiesContext는 나를위한 해결책이 아니 었습니다. 왜냐하면 전체 응용 프로그램 컨텍스트가 파괴되기 때문에 각 테스트 후에 만들어야하기 때문입니다.

    @ 전에는 각 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
    }
    }
    

    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 {
    }
    

    마지막으로 테스트를 작성할 수 있으며 데이터베이스가 클린 모드인지 확인할 수 있습니다.

    @RunWith(SpringRunner.class)
    @OurderTest
    public class ProductSaveServiceIntTest {
     }
    

    편집 : 내 솔루션을 조금 향상되었습니다. 나는 언젠가 하나의 테스트 메소드가 테스트 클래스 내의 다가오는 모든 테스트에 대해 내 데이터베이스를 파괴했다는 문제점을 안고 있었다. 그래서 주석을 만들었습니다.

    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 {
    }
    

    이것을 CleanupDatabaseTestExectionListener에 추가했습니다.

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

    이 두 조각의 도움으로 나는 이제 다음과 같은 테스트를 만들 수 있습니다.

    @Test
    @ClearContext
    public void testWhichDirtiesDatabase() {}
    
  3. ==============================

    3.org.springframework.transaction.annotation.Transactional의 Junit 클래스 레벨에서 @Transactional 어노테이션을 사용할 수있다.

    org.springframework.transaction.annotation.Transactional의 Junit 클래스 레벨에서 @Transactional 어노테이션을 사용할 수있다.

    예 :

    package org.test
    import org.springframework.transaction.annotation.Transactional;
    
    @Transactional
    public class ArmyTest{
    
    }
    
  4. ==============================

    4.데이터베이스에서 모든 데이터를 삭제하는 @Before 메서드를 만듭니다. Hibernate를 사용하고 있으므로 HQL : Contract에서 delete를 사용할 수 있습니다.

    데이터베이스에서 모든 데이터를 삭제하는 @Before 메서드를 만듭니다. Hibernate를 사용하고 있으므로 HQL : Contract에서 delete를 사용할 수 있습니다.

  5. from https://stackoverflow.com/questions/14343893/how-do-i-reset-my-database-state-after-each-unit-test-without-making-the-whole-t by cc-by-sa and MIT license