简体   繁体   English

使用 Spring Boot 和 Liquibase 时,如何在每次集成测试后清理数据库表?

[英]How to clean database tables after each integration test when using Spring Boot and Liquibase?

I have a side project were I'm using Spring Boot, Liquibase and Postgres.我有一个附带项目,我正在使用 Spring Boot、Liquibase 和 Postgres。

I have the following sequence of tests:我有以下测试序列:

test1();
test2();
test3();
test4();

In those four tests I'm creating the same entity.在这四个测试中,我正在创建相同的实体。 As I'm not removing the records from the table after each test case, I'm getting the following exception: org.springframework.dao.DataIntegrityViolationException由于我没有在每个测试用例之后从表中删除记录,因此出现以下异常: org.springframework.dao.DataIntegrityViolationException

I want to solve this problem with the following constraints:我想通过以下约束来解决这个问题:

  1. I don't want to use the @repository to clean the database.我不想使用@repository来清理数据库。
  2. I don't want to kill the database and create it on each test case because I'm using TestContainers and doing that would increase the time it takes to complete the tests.我不想杀死数据库并在每个测试用例上创建它,因为我正在使用 TestContainers,这样做会增加完成测试所需的时间。

In short: How can I remove the records from one or more tables after each test case without 1) using the @repository of each entity and 2) killing and starting the database container on each test case?简而言之:如何在每个测试用例之后从一个或多个表中删除记录,而无需 1)使用每个实体的@repository和 2)在每个测试用例上杀死并启动数据库容器?

The simplest way I found to do this was the following:我发现执行此操作的最简单方法如下:

  1. Inject a JdbcTemplate instance注入一个 JdbcTemplate 实例
@Autowired
private JdbcTemplate jdbcTemplate;
  1. Use the class JdbcTestUtils to delete the records from the tables you need to.使用 class JdbcTestUtils 从您需要的表中删除记录。
JdbcTestUtils.deleteFromTables(jdbcTemplate, "table1", "table2", "table3");
  1. Call this line in the method annotated with @After or @AfterEach in your test class:在您的测试 class 中使用@After@AfterEach注释的方法中调用此行:
@AfterEach
void tearDown() throws DatabaseException {
    JdbcTestUtils.deleteFromTables(jdbcTemplate, "table1", "table2", "table3");
}

I found this approach in this blog post: Easy Integration Testing With Testcontainers我在这篇博文中找到了这种方法: 使用 Testcontainers 进行轻松集成测试

Annotate your test class with @DataJpaTest .使用 @DataJpaTest 注释您的测试@DataJpaTest From the documentation:从文档中:

By default, tests annotated with @DataJpaTest are transactional and roll back at the end of each test.默认情况下,使用 @DataJpaTest 注释的测试是事务性的,并在每个测试结束时回滚。 They also use an embedded in-memory database (replacing any explicit or usually auto-configured DataSource).他们还使用嵌入式内存数据库(替换任何显式或通常自动配置的数据源)。

For example using Junit4:例如使用 Junit4:

@RunWith(SpringRunner.class)
@DataJpaTest
public class MyTest { 
//...
}

Using Junit5:使用 Junit5:

@DataJpaTest
public class MyTest { 
//...
}

You could use @Transactional on your test methods.您可以在测试方法上使用 @Transactional。 That way, each test method will run inside its own transaction bracket and will be rolled back before the next test method will run.这样,每个测试方法都将在其自己的事务括号内运行,并在下一个测试方法运行之前回滚。

Of course, this only works if you are not doing anything weird with manual transaction management, and it is reliant on some Spring Boot autoconfiguration magic, so it may not be possible in every use case, but it is generally a highly performant and very simple approach to isolating test cases.当然,这仅在您没有对手动事务管理做任何奇怪的事情时才有效,并且它依赖于一些 Spring 引导自动配置魔术,因此可能无法在每个用例中使用,但它通常是高性能且非常简单的隔离测试用例的方法。

i think this is the most effecient way for postgreSQL.我认为这是 postgreSQL 最有效的方法。 You can make same thing for other db.你可以为其他数据库做同样的事情。 Just find how to restart tables sequence and execute it只需找到如何重新启动表序列并执行它

@Autowired
private JdbcTemplate jdbcTemplate;

@AfterEach
public void execute() {
    jdbcTemplate.execute("TRUNCATE TABLE users" );
    jdbcTemplate.execute("ALTER SEQUENCE users_id_seq RESTART");
}

My personal preference would be:我个人的偏好是:

private static final String CLEAN_TABLES_SQL[] = {
    "delete from table1",
    "delete from table2",
    "delete from table3"
};

@After
public void tearDown() {
    for (String query : CLEAN_TABLES_SQL)
    {
        getJdbcTemplate().execute(query);
    }
}

To be able to adopt this approach, you would need to extend the class with DaoSupport, and set the DataSource in the constructor.为了能够采用这种方法,您需要使用 DaoSupport 扩展 class,并在构造函数中设置 DataSource。

public class Test extends NamedParameterJdbcDaoSupport

public Test(DataSource dataSource)
{
    setDataSource(dataSource);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM