web-dev-qa-db-ja.com

テスト全体をトランザクションにせずに、各単体テストの後にデータベースの状態をリセットするにはどうすればよいですか?

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

サービスへの呼び出しは3つあり( "m_contractService.save"、 "m_contractService.save"、および "m_contractService.findContractByOppId")、それぞれがトランザクションとして扱われます。しかし、各単体テストの後で、インメモリデータベースを元の状態にリセットする方法がわかりません。

追加情報が必要な場合はお知らせください。

24
Dave

Hibernateを使用しているので、プロパティhibernate.hbm2ddl.autoを使用して、起動時に毎回データベースを作成できます。また、各テスト後にスプリングコンテキストを強制的にリロードする必要があります。 @DirtiesContextアノテーションでこれを行うことができます。

これはテストに少し余分なオーバーヘッドを追加する可能性があるため、他の解決策は、各テーブルからデータを手動で削除することです。

20
Solubris

@DirtiesContextは、アプリケーションコンテキスト全体が破棄されるため、解決策ではありませんでした。テストごとに作成する必要があります->非常に長くかかりました。

@Beforeも作成する必要があるため、私にとっては良い解決策ではありませんでした@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 {
 }

編集:私は私のソリューションを少し改善しました。あるテストメソッドが、テストクラス内のすべての今後のテストでデータベースを破壊するという問題がありました。だから私は注釈を作成しました

パッケージ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);
}

これらの2つのスニペットを使用して、次のようなテストを作成できます。

@Test
@ClearContext
public void testWhichDirtiesDatabase() {}
9
Simon Ludwig

@Transactionalアノテーションは、JUnitクラスレベルでorg.springframework.transaction.annotation.Transactionalから使用できます。

例えば:

package org.test
import org.springframework.transaction.annotation.Transactional;

@Transactional
public class ArmyTest{

}
2
Arun

テストごとにランダムメモリデータベースを使用して同じ問題を解決します。

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@TestPropertySource(properties = {
    "spring.datasource.url=jdbc:hsqldb:mem:${random.uuid}"
})
0
TangZero

作る @Beforeメソッド。データベースからすべてのデータを削除します。 HQLを使用できるようにHibernateを使用しています:delete from Contract

0
user1137032