Подтвердить что ты не робот

Как я reset состояние моей базы данных после каждого unit test, не делая весь тест транзакцией?

Я использую 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" ), и каждый из них рассматривается как транзакция, которую я хочу. Но я не знаю, как reset моя база данных в памяти в исходное состояние после каждого unit test.

Сообщите мне, если мне нужно предоставить дополнительную информацию.

4b9b3361

Ответ 1

Поскольку вы используете спящий режим, вы можете использовать свойство hibernate.hbm2ddl.auto для создания базы данных при запуске каждый раз. Вам также потребуется принудительно перезагрузить контекст spring после каждого теста. Вы можете сделать это с помощью аннотации @DirtiesContext.

Это может добавить к вашим испытаниям дополнительные накладные расходы, поэтому другое решение состоит в том, чтобы просто вручную удалять данные из каждой таблицы.

Ответ 2

@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);
}

с помощью этих двух фрагментов я теперь могу создавать такие тесты:

@Test
@ClearContext
public void testWhichDirtiesDatabase() {}

Ответ 3

Сделайте метод @Before, в котором вы удаляете все данные из базы данных. Вы используете Hibernate, чтобы использовать HQL: delete from Contract.

Ответ 4

Вы можете использовать аннотацию @Transactional на уровне класса Junit от org.springframework.transaction.annotation.Transactional.

Например:

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

@Transactional
public class ArmyTest{

}