@BeforeAll JUnit/spring-boot-test 在应用程序上下文启动时运行的替代方案

@BeforeAll JUnit/spring-boot-test alternative that runs when application context starts

我正在编写一个利用嵌入式数据库的 @Repository/@Service 集成测试。在我的测试中 class,我想用一些数据预加载我的数据库。

我目前正在使用 @BeforeEach 加载我的示例数据,但是,此代码在我的 class.

中的每个测试中都是 运行

有什么方法可以在 Spring 应用程序上下文加载后但在任何测试 运行 之前加载我的测试数据?

我目前的做法:

@BeforeEach
public void before() {
    repository.save(...); // -> prepopulates repository with sample data
}

@Test
public void testService() {
    service.get(...); // -> gathers existing record
}

@Test
public void deleteById() {
    service.delete(...); // -> deletes existing record
}

但是...因此,我需要在每次测试后清除记录。否则很容易违反任何唯一约束。

而不是在每次测试之前使用 运行 所需的 @BeforeEach...是否可以在 @BeforeAll 之后以 @BeforeAll 的方式加载它=31=]应用程序上下文已经加载?

在这种情况下,我将为测试创建一个构造函数 class。它会在一切之前触发。

@BeforeEach 在每次测试之前运行,但在所有初始化之后运行。

您也可以只使用 Mockito 并模拟结果,而无需清理和过度复杂化

只需将以下代码段添加到您的代码中。这就像您检测 Spring 应用程序是否真正启动一样。

@Configuration
public class AppConfig implements ApplicationListener<ApplicationReadyEvent> {

    /**
     * This is to indicate in the logs when the application has actually started and everything is loaded.
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        ApplicationContext context = event.getApplicationContext();
        Environment env = context.getEnvironment();
        // do what you want on application start
    }
}

P.S. 对于测试中的数据库操作 @Sql 是评论中提到的最佳候选。

Is there any way that I can load in my test data after Spring application context has loaded

基本上是的,我认为你可以做到:

想法是在启动应用程序上下文时或在启动过程中加载 SQL 数据。

例如,spring 与 Flyway 的引导集成就是这样工作的(创建并加载 Flyway 的 bean)。因此,理论上,您可以仅将 Flyway 与测试迁移一起使用,其中将包含测试数据生成的所有相关 SQL 脚本。

技术上如何做到这一点?

这是一种方法:

创建一个特殊的 bean(就像它与 Flyway 的工作方式一样),这将取决于您的存储库并在 post 构造中保存数据:

@Component
public class SqlGenerationBean {

   @Autowired
   private MyRepository repo;

   @PostConstruct
   public void init() {
      repo.save();
   } 
}

另一种方法是创建一个侦听器,它将在应用程序上下文启动时调用并再次调用相同的 repo.save()

在这两种情况下,bean/listener 代码都不应从生产环境中访问(它仅用于测试):因此将其放在 src/test/java 下面的某个地方,例如

现在一旦应用程序上下文启动,您就可以使用一个巧妙的技巧:

使用 @Transactional 注释标记您的测试。 Spring 会将代码包装在一个人工事务中,该事务将自动回滚(即使测试成功),以便您在测试期间修改的所有数据都将回滚,基本上在每次测试之前,您'将具有相同的状态(即与数据库的状态相同 when/after 应用程序上下文启动)。当然,如果你在测试中使用DDL,有些数据库无法将其作为事务的一部分,但它确实取决于数据库。

这里另一个有趣的地方是应用程序上下文甚至可以在测试用例之间缓存(只创建一次),所以请记住这一点。