简体   繁体   English

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

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

I'm writing a @Repository/@Service integration test that leverages an embedded database.我正在编写一个利用嵌入式数据库的@Repository/@Service集成测试。 In my test class, I would like to preload my database with some data.在我的测试课中,我想用一些数据预加载我的数据库。

I'm currently using @BeforeEach to load in my sample data, however, this code is run upon each test in my class.我目前正在使用@BeforeEach加载我的示例数据,但是,此代码在我班级的每个测试中运行。

Is there any way that I can load in my test data after Spring application context has loaded, but before any test has been run?有什么方法可以在 Spring 应用程序上下文加载之后但在任何测试运行之前加载我的测试数据?

My current approach:我目前的做法:

@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
}

However... with this, I am required to flush out the records after every test.但是......有了这个,我需要在每次测试后刷新记录。 Otherwise any unique constraints can easily be violated.否则很容易违反任何唯一约束。

Rather than using @BeforeEach which is required to run before every test... is it possible to load this in in a @BeforeAll kind of fashion that happens after the spring application context has been loaded?而不是使用@BeforeEach需要在每次测试之前运行......是否可以在加载 spring 应用程序上下文之后以@BeforeAll类型的方式加载它?

Just add following snippet to your code.只需将以下代码段添加到您的代码中。 This is just like you can do to detect that Spring application is really started.这就像您可以检测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
    }
}

PS For database manipulation in test @Sql is the best candidate as was mentioned in comment. PS对于测试中的数据库操作, @Sql是评论中提到的最佳候选者。

Is there any way that I can load in my test data after Spring application context has loaded在加载 Spring 应用程序上下文后,有什么方法可以加载我的测试数据

Basically yes, I think you can do that:基本上是的,我认为你可以这样做:

The idea is to load the SQL data when the application context is started or in the process of being started.这个想法是在应用程序上下文启动或正在启动的过程中加载 SQL 数据。

For example, spring boot integration with Flyway works this way (the bean of Flyway is created and loaded).例如,spring boot 与 Flyway 的集成就是这样工作的(Flyway 的 bean 被创建和加载)。 So, in theory, you could merely use Flyway with test migrations that will contain all the relevant SQL scripts of test data generation.因此,理论上,您可以仅将 Flyway 与包含测试数据生成的所有相关 SQL 脚本的测试迁移一起使用。

How can you do this technically?你怎么能在技术上做到这一点?

Here is one way:这是一种方法:

Create a special bean (just like the way it works with Flyway) that would depend on your repository and in post construct save the data:创建一个特殊的 bean(就像它与 Flyway 的工作方式一样),它取决于您的存储库并在后期构造中保存数据:

@Component
public class SqlGenerationBean {

   @Autowired
   private MyRepository repo;

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

Another way of doing is to create a listener that will be called upon the application context started and again will call the same repo.save() .另一种做法是创建一个侦听器,该侦听器将在应用程序上下文启动时调用,并再次调用相同的repo.save()

In both cases the bean/listener code should not be accessible from production (it's only for tests): so put it somewhere under src/test/java for example在这两种情况下,bean/侦听器代码不应该从生产中访问(它仅用于测试):因此将它放在src/test/java下的某个位置,例如

Now once the application context is started you can use a neat trick:现在一旦应用程序上下文启动,您就可以使用一个巧妙的技巧:

Mark your tests with @Transactional annotation.使用@Transactional注释标记您的测试。 Spring will wrap the code in an artificial transaction that will be rolled back automatically (even if the test succeeds) so that all the data that you'll modify during the test will be rolled back and basically before each test, you'll have the same state (that is identical to the state of the database when/after the application context starts). Spring 会将代码包装在一个人工事务中,该事务将自动回滚(即使测试成功),以便您在测试期间修改的所有数据都将回滚,并且基本上在每次测试之前,您将拥有相同的状态(即与应用程序上下文启动时/之后的数据库状态相同)。 Of course, if you use DDL in the test, some databases can't make it a part of transaction but it depends on the database really.当然,如果在测试中使用DDL,有些数据库不能将其作为事务的一部分,但它确实取决于数据库。

Another interesting point here is that the application context can be cached even between the test cases (created only once), so keep this in mind.这里另一个有趣的点是,即使在测试用例之间(仅创建一次),应用程序上下文也可以缓存,因此请记住这一点。

In this case I would just create a constructor for the test class.在这种情况下,我只会为测试类创建一个构造函数。 It will be triggered before everything.它会在一切发生之前被触发。

@BeforeEach runs before each tests but after all initialisations . @BeforeEach 在每次测试之前运行,但在所有初始化之后运行。

you can also just use Mockito and mock the result without need to clean and overcomplicate您也可以只使用 Mockito 并模拟结果,而无需清理和过度复杂化

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

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