How to re-create database before each test in Spring?

可紊 提交于 2019-11-28 05:16:59

Actually, I think you want this:

@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 may be used as a class-level and method-level annotation within the same class. In such scenarios, the ApplicationContext will be marked as dirty after any such annotated method as well as after the entire class. If the DirtiesContext.ClassMode is set to AFTER_EACH_TEST_METHOD, the context will be marked dirty after each test method in the class.

To create the database you have to do what the other answers say with the spring.jpa.hibernate.ddl-auto=create-drop, now if your intent is to pupulate the database on each test then spring provides a very usefull anotation

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

that is from this package org.springframework.test.context.jdbc.Sql; and you can run a before test method and a after test method. To populate the database.

Regarding creating the database each time, Say you only want your Test to have the create-drop option you can configure your tests with a custom properties with this annotation

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

Hope it helps

With spring boot the h2 database can be defined uniquely for each test. Just override the data source URL for each test

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

The tests can run in parallel.

Within the test the data can be reset by

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)

If you use spring.jpa.hibernate.ddl-auto=create-drop should be enough to create/drop database?

Unless you're using some kind of Spring-Data integration (which I don't know at all), this seems like custom logic you'll need to implement yourself. Spring doesn't know about your databases, its schemas, and tables.

Assuming JUnit, write appropriate @Before and @After methods to set up and clean up your database, its tables, and data. Your tests can themselves write the data they need, and potentially clean up after themselves if appropriate.

If you are looking for an alternative for the @DirtiesContext, this code below will help you. I used some code from this answer.

First, setup the H2 database on the application.yml file on your test resources folder:

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

After that, create a class called ResetDatabaseTestExecutionListener:

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

The code above will reset the database (truncate tables, reset sequences, etc) and is prepared to work with H2 database. If you are using another memory database (like HsqlDB) you need to make the necessary changes on the SQLs queries to accomplish the same thing.

After that, go to your test class and add the @TestExecutionListeners annotation, like:

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

This should work.

Honestly, I not see any performance difference using this approach or @DirtiesContext, but maybe on a bigger application this can improve the integration test performance.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!