【问题标题】:How to clean database tables after each integration test when using Spring Boot and Liquibase?使用 Spring Boot 和 Liquibase 时如何在每次集成测试后清理数据库表?
【发布时间】:2020-11-02 07:46:07
【问题描述】:

我有一个附带项目,我正在使用 Spring Boot、Liquibase 和 Postgres。

我有以下测试序列:

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

在这四个测试中,我创建了相同的实体。因为我没有在每个测试用例之后从表中删除记录,所以我得到了以下异常:org.springframework.dao.DataIntegrityViolationException

我想通过以下约束来解决这个问题:

  1. 我不想使用@repository 来清理数据库。
  2. 我不想杀死数据库并在每个测试用例上创建它,因为我正在使用 TestContainers,这样做会增加完成测试所需的时间。

简而言之:如何在没有 1) 使用每个实体的 @repository 和 2) 在每个测试用例上杀死并启动数据库容器的情况下,在每个测试用例之后从一个或多个表中删除记录?

【问题讨论】:

    标签: java spring-boot liquibase testcontainers


    【解决方案1】:

    我发现最简单的方法如下:

    1. 注入 JdbcTemplate 实例
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    1. 使用 JdbcTestUtils 类从您需要的表中删除记录。
    JdbcTestUtils.deleteFromTables(jdbcTemplate, "table1", "table2", "table3");
    
    1. 在您的测试类中用@After@AfterEach 注释的方法中调用此行:
    @AfterEach
    void tearDown() throws DatabaseException {
        JdbcTestUtils.deleteFromTables(jdbcTemplate, "table1", "table2", "table3");
    }
    

    我在这篇博文中发现了这种方法: Easy Integration Testing With Testcontainers

    【讨论】:

      【解决方案2】:

      使用@DataJpaTest 注释您的测试类。来自文档:

      默认情况下,使用 @DataJpaTest 注释的测试是事务性的,并在每个测试结束时回滚。他们还使用嵌入式内存数据库(替换任何显式或通常自动配置的数据源)。

      例如使用 Junit4:

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

      使用 Junit5:

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

      【讨论】:

      • 可能比我的回答好。
      • @user7655213,我不同意并认为您的方法应该是第一件事,首先将测试声明为@Transactional。如果这不起作用,请尝试其他替代方案。 @DataJpaTest 专门用于仅关注 JPA 功能的测试,如文档所述:docs.spring.io/spring-boot/docs/current/api/org/springframework/…
      • @Ricci 随时发布您的答案,这可能对其他人有帮助。
      • This 来自 reflectoring.io 的文章提供了一些关于 @DataJpaTest 注释的重要额外信息(以及有关“使用 Spring Boot 和 @DataJpaTest 测试 JPA 查询”的更多信息)
      • 不要在测试中使用事务性
      【解决方案3】:

      你可以在你的测试方法上使用@Transactional。这样,每个测试方法都将在其自己的事务括号内运行,并在下一个测试方法运行之前回滚。

      当然,这只有在你没有对手动事务管理做任何奇怪的事情时才有效,并且它依赖于一些 Spring Boot 自动配置魔法,因此可能无法在每个用例中使用,但它通常是高性能的以及隔离测试用例的非常简单的方法。

      【讨论】:

        【解决方案4】:

        我认为这是 postgreSQL 最有效的方式。你可以为其他数据库做同样的事情。只需找到如何重新启动表序列并执行它

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

        【讨论】:

          【解决方案5】:

          我个人的偏好是:

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

          为了能够采用这种方法,您需要 使用 DaoSupport 扩展类,并在构造函数中设置 DataSource。

          public class Test extends NamedParameterJdbcDaoSupport
          
          public Test(DataSource dataSource)
          {
              setDataSource(dataSource);
          }
          

          【讨论】:

            猜你喜欢
            • 2019-07-17
            • 1970-01-01
            • 2018-11-27
            • 2021-12-11
            • 1970-01-01
            • 2019-05-30
            • 2016-06-30
            • 1970-01-01
            • 2019-04-04
            相关资源
            最近更新 更多