Spring boot 和 Flyway:集成测试前清除数据库数据

Posted

技术标签:

【中文标题】Spring boot 和 Flyway:集成测试前清除数据库数据【英文标题】:Spring boot and Flyway: Clear database data before integration tests 【发布时间】:2018-11-27 16:15:35 【问题描述】:

我正在使用 Spring Boot 框架 (v2.0.2) 构建 REST 服务,其中我在数据库上运行了集成测试。我用谷歌搜索了很多,有很多关于在测试前清理数据库的文章,但不幸的是,我发现它们要么效率低下,要么被黑客入侵,而且不适用于 Spring boot。请您多多包涵并提出解决此问题的好方法吗?

理想情况下,我认为不应该在每次测试之前清除数据库,而是在其中一些测试之前清除数据库,例如套件或每个测试类。找到的建议之一如下所示:

@Autowired
protected Flyway flyway;

@Before
public void init() 
    flyway.clean();
    flyway.migrate();

在每次测试之前重建数据库,显然效率不高。将其更改为静态上下文并使用 @BeforeClass 不起作用,因为 Spring 不注入静态字段。

有没有什么好方法可以从静态上下文中访问这个 flyway bean,以使这个解决方案发挥作用?

这里的子问题: Flyway 有一个命令 clean,它不仅会清除数据,还会删除所有内容,然后 migrate 命令再次执行迁移。这似乎也是开销。由于无论如何都会在启动时检查迁移,因此我认为没有必要在每个测试组之前拆除并重建所有内容。只需清除数据就足够了。您能否就如何实现这一目标提出一些建议?

总而言之,我正在寻找一种在每组集成测试(例如每个类)之前删除数据库数据(如果可能,不是表)的标准方法。我想每个人在使用 Spring boot 时都会面临这个任务,所以也许框架本身考虑了一些不错的解决方案。

谢谢!

【问题讨论】:

为什么不在你的测试中使用@Transactional。这样数据库总是干净的。 为什么不使用简单的删除或截断表?您可以选择要删除哪个表?您还可以修复顺序,以便处理外键约束。如果你想玩图书馆,你可以看看 DBUnit。 @lealceldeiro 感谢您的建议。可能我不使用事务的主要原因是我想在运行测试组后拥有一个填充的数据库。它对调试有很大帮助,也有助于为客户端应用程序填充数据。另一个原因是我觉得这不是它的目的。在我的业务逻辑中实现事务之后,我最终会遇到嵌套事务,并且可能会遇到许多棘手的问题,因为它的大部分实现都在后台。 @gagansingh 如果我能找到一种与 spring 结合的方法,那就太好了。你会在哪里添加这个逻辑?您会使用@Sql 注释并将这些清理脚本放在一个sql 文件中吗?也许将此注释添加到测试类? 【参考方案1】:

您可以为您的测试创建配置文件。它会在所有测试之前运行一次。

@Configuration
public class TestConfig 
@Bean
public FlywayMigrationStrategy clean() 
    return flyway -> 
        flyway.clean();
        flyway.migrate();
    ;


【讨论】:

非常好的解决方案。谢谢 :) 我想我会将它与@Sql 注释结合使用。我将在所有测试之前重置数据库并测试所有迁移,然后 - 在每组集成测试之前,我将只清除带有 @Sql 注释中的 sql 脚本的表中的数据。【参考方案2】:

这个答案很有用,但它并没有让我一直到那里,所以我想我会回来并添加一个答案,以防其他人正在寻求解决同样的问题。上面的 bean 定义很棒。

弹簧型材有 5 种左右的可能性。我查看了文档以及人们如何使用它们,但走了另一条路。 Maven 有 6 个作用域,但在这种情况下有用的是运行时和测试。

当我深入研究弹簧轮廓以及可以在它们之间切换的各种方式时,我的情况似乎有点太复杂了。我只希望创建、构建并填充一些数据来测试我的数据库,以便我可以在我的 jpa spring boot 应用程序中测试存储库。我不想花 4 个小时来设置个人资料。并不是说从长远来看它不值得努力,只是我想让事情进展顺利。

当我执行 spring-boot:run 时,我希望迁移非测试数据库,但我不希望其中有任何用于测试的 crud 数据。

因此,在实时应用程序中,我想要一个几乎空的数据库,并且在测试期间,我想要 flyway 清理数据库,运行版本化迁移并使用测试数据填充它。

上面的答案让我找到了一个解决方案,随着我的项目接近生产,我可能会将其折叠成弹簧型材。

原来 spring-boot-test 提供了一个 @TestConfiguration 注释,您可以将其附加到 src/test/ 层次结构中的任何类。我创建了一个 FlywayConfiguration 类,其中包含上面提供的 bean 定义:

package com.foo.fooservice;

import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;

@TestConfiguration
public class FlywayMigrationConfig 

@Bean
public static FlywayMigrationStrategy cleanMigrateStrategy()

        return flyway -> 
                flyway.clean();
                flyway.migrate();
                ;
        

所以现在,如果我想在测试中使用它,我会在适当的测试类中添加另一个漂亮的注释 - @Includes,@TestConfiguration 注释的伴侣 - 这样我就可以以与我相同的方式使用此配置可能像这样使用@BeforeClass:

@DataJpaTest
@Import(FlywayMigrationConfig.class) 
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class AccountUserRepoTest 



    @Autowired 
    private AccountUserRepo accountUserRepo;

    @Autowired
    private FlywayMigrationStrategy strategy;

这使我可以在每个测试类的基础上注入这种飞行迁移策略。 Spring 不会将您的 bean 自动注入到每个测试类中,您现在只需将 @Includes 注释添加到适当的测试类即可使用此配置。您不必在要使用它的每个测试类中定义 bean。只需使用@Includes(your@TestConiguration-annoted-class)。

我碰巧使用的是 postgresSQL 而不是 H2,因为我认为如果我正在对存储库实体进行集成测试,我不妨对照我将在生产中使用的内容进行测试。

另外:src/main/resources 将 jdbc 和 flyway 属性设置为 dev 模式名称和 jdbc url。

src/test/resources/application.properties 将架构名称设置为“test”(您可以随意命名)。

您可能不希望这种方法的一个缺点是粒度 - 为您以这种方式配置的每个测试类清理和重新填充数据库。

我个人喜欢这样,因为对于我正在测试的每个存储库类,我都希望刷新数据。如果我正在处理特定的测试类,我也喜欢这样,在这种粒度级别进行配置意味着“运行测试”可以开箱即用。无需在 IDE 中进行特殊配置即可使其工作。

【讨论】:

以上是关于Spring boot 和 Flyway:集成测试前清除数据库数据的主要内容,如果未能解决你的问题,请参考以下文章

Flyway 和 Spring Boot 集成

带有用户名和密码的 Zonky + Spring Boot + Postgres + Flyway

Flyway 与 Spring Boot 的集成不会在嵌入式 H2 数据库上执行迁移脚本

使用 Flyway 和 Spring Boot 迁移基线

Spring Boot 集成 Flyway,数据库也能做版本控制,太牛逼了!

使用 Flyway 和 Spring Boot 在 docker testcontainers 环境中迁移模式