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")、それぞれがトランザクションとして扱われます。しかし、各単体テストの後で、インメモリデータベースを元の状態にリセットする方法がわかりません。
追加情報が必要な場合はお知らせください。
Hibernateを使用しているので、プロパティhibernate.hbm2ddl.autoを使用して、起動時に毎回データベースを作成できます。また、各テスト後にスプリングコンテキストを強制的にリロードする必要があります。 @DirtiesContextアノテーションでこれを行うことができます。
これはテストに少し余分なオーバーヘッドを追加する可能性があるため、他の解決策は、各テーブルからデータを手動で削除することです。
@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() {}
@Transactional
アノテーションは、JUnitクラスレベルでorg.springframework.transaction.annotation.Transactional
から使用できます。
例えば:
package org.test
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class ArmyTest{
}
テストごとにランダムメモリデータベースを使用して同じ問題を解決します。
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
@TestPropertySource(properties = {
"spring.datasource.url=jdbc:hsqldb:mem:${random.uuid}"
})
作る @Before
メソッド。データベースからすべてのデータを削除します。 HQLを使用できるようにHibernateを使用しています:delete from Contract
。