Как воссоздать базу данных перед каждым тестом в Spring?

My Spring -Boot-Mvc-Web-приложение имеет следующую конфигурацию базы данных в файле application.properties:

spring.datasource.url=jdbc:h2:tcp://localhost/~/pdk
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

Это единственная конфигурация, которую я сделал. Никаких других конфигураций, сделанных мной нигде. Тем не менее Spring и подсистемы автоматически воссоздают базу данных при каждом запуске веб-приложения. База данных воссоздается именно при запуске системы, пока она содержит данные после завершения приложения.

Я не понимал эти значения по умолчанию и ожидал, что это подходит для тестов.

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

Итак, вопрос: как сделать какой-то смысл? I.e., как сделать базу данных воссозданной перед каждым тестом, как это происходит при первом запуске приложения?

Заголовок моего тестового класса:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = myapp.class)
//@WebAppConfiguration
@WebIntegrationTest
@DirtiesContext
public class WebControllersTest {

Как вы видите, я пробовал @DirtiesContext на уровне класса, и это не помогло.

UPDATE

У меня есть bean

@Service
public class DatabaseService implements InitializingBean {

который имеет метод

@Override
    @Transactional()
    public void afterPropertiesSet() throws Exception {
        log.info("Bootstrapping data...");
        User user = createRootUser();
        if(populateDemo) {
            populateDemos();
        }
        log.info("...Bootstrapping completed");
    }

Теперь я применил метод populateDemos() для очистки всех данных из базы данных. К сожалению, он не вызывается перед каждым тестом, несмотря на @DirtiesContext. Почему?

Ответы

Ответ 1

На самом деле, я думаю, вы этого хотите:

@DirtiesContext(classMode = ClassMode.BEFORE_EACH_TEST_METHOD)

http://docs.spring.io/autorepo/docs/spring-framework/4.2.6.RELEASE/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

@DirtiesContext может использоваться как уровень уровня и уровня метода аннотации в пределах одного класса. В таких сценариях ApplicationContext будет помечен как грязный после любого такого аннотированного метод, а также после всего класса. Если DirtiesContext.ClassMode устанавливается в AFTER_EACH_TEST_METHOD, контекст будет отмечен как загрязненный после каждого метода тестирования в классе.

Ответ 2

Чтобы создать базу данных, вы должны делать то, что говорят другие ответы с помощью spring.jpa.hibernate.ddl-auto=create-drop, теперь, если ваше намерение состоит в том, чтобы окупить базу данных для каждого теста, тогда spring обеспечивает очень полезную анотацию

@Transactional(value=JpaConfiguration.TRANSACTION_MANAGER_NAME)
@Sql(executionPhase=ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:/test-sql/group2.sql")
public class GroupServiceTest extends TimeoffApplicationTests {

который из этого пакета org.springframework.test.context.jdbc.Sql;, и вы можете запустить метод перед тестом и метод после теста. Чтобы заполнить базу данных.

Что касается создания базы данных каждый раз, скажите, что вы хотите, чтобы в вашем тесте была опция create-drop, вы можете настроить свои тесты с помощью специальных свойств с помощью этой аннотации

@TestPropertySource(locations="classpath:application-test.properties")
public class TimeoffApplicationTests extends AbstractTransactionalJUnit4SpringContextTests{

Надеюсь, что это поможет

Ответ 3

При весенней загрузке базу данных h2 можно определить уникально для каждого теста. Просто переопределите URL источника данных для каждого теста

 @SpringBootTest(properties = {"spring.config.name=myapp-test-h2","myapp.trx.datasource.url=jdbc:h2:mem:trxServiceStatus"})

Тесты могут выполняться параллельно.

В рамках теста данные могут быть сброшены

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)

Ответ 4

Если вы используете spring.jpa.hibernate.ddl-auto=create-drop, должно быть достаточно для создания/удаления базы данных?

Ответ 5

Если вы ищете альтернативу для @DirtiesContext, этот код ниже поможет вам. Я использовал код из этого ответа.

Сначала настройте базу данных H2 для файла application.yml в папке с тестовыми ресурсами:

spring: 
  datasource:
    platform: h2
    url: jdbc:h2:mem:test
    driver-class-name: org.h2.Driver
    username: sa
    password:

После этого создайте класс с именем ResetDatabaseTestExecutionListener:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;

public class ResetDatabaseTestExecutionListener extends AbstractTestExecutionListener {

    @Autowired
    private DataSource dataSource;

    public final int getOrder() {
        return 2001;
    }

    private boolean alreadyCleared = false;

    @Override
    public void beforeTestClass(TestContext testContext) {
        testContext.getApplicationContext()
                .getAutowireCapableBeanFactory()
                .autowireBean(this);
    }

    @Override
    public void prepareTestInstance(TestContext testContext) throws Exception {

        if (!alreadyCleared) {
            cleanupDatabase();
            alreadyCleared = true;
        }
    }

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

    private void cleanupDatabase() throws SQLException {
        Connection c = dataSource.getConnection();
        Statement s = c.createStatement();

        // Disable FK
        s.execute("SET REFERENTIAL_INTEGRITY FALSE");

        // Find all tables and truncate them
        Set<String> tables = new HashSet<>();
        ResultSet rs = s.executeQuery("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES  where TABLE_SCHEMA='PUBLIC'");
        while (rs.next()) {
            tables.add(rs.getString(1));
        }
        rs.close();
        for (String table : tables) {
            s.executeUpdate("TRUNCATE TABLE " + table);
        }

        // Idem for sequences
        Set<String> sequences = new HashSet<>();
        rs = s.executeQuery("SELECT SEQUENCE_NAME FROM INFORMATION_SCHEMA.SEQUENCES WHERE SEQUENCE_SCHEMA='PUBLIC'");
        while (rs.next()) {
            sequences.add(rs.getString(1));
        }
        rs.close();
        for (String seq : sequences) {
            s.executeUpdate("ALTER SEQUENCE " + seq + " RESTART WITH 1");
        }

        // Enable FK
        s.execute("SET REFERENTIAL_INTEGRITY TRUE");
        s.close();
        c.close();
    }
}

Приведенный выше код сбросит базу данных (усеченные таблицы, последовательности сброса и т.д.) и готов к работе с базой данных H2. Если вы используете другую базу данных памяти (например, HsqlDB), вам нужно внести необходимые изменения в запросы SQLs, чтобы выполнить то же самое.

После этого перейдите в свой тестовый класс и добавьте аннотацию @TestExecutionListeners, например:

@TestExecutionListeners(mergeMode =
        TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS,
        listeners = {ResetDatabaseTestExecutionListener.class}
)
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CreateOrderIT {

Это должно работать.

Если вы не видите никакой разницы в производительности между этим подходом и @DirtiesContext, , вероятно, вы используете @MockBean внутри своих тестов, что помечает контекст как грязный и автоматически перезагружает контекст Spring.

Ответ 6

Если вы не используете какую-то Spring -Data-интеграцию (которой я вообще не знаю), это похоже на пользовательскую логику, которую вам нужно реализовать самостоятельно. Spring не знает о ваших базах данных, его схемах и таблицах.

Предполагая JUnit, напишите подходящие методы @Before и @After для настройки и очистки базы данных, ее таблиц и данных. Ваши тесты сами могут записать нужные им данные и, возможно, потенциально очистить их после себя.