我正在编写一个利用嵌入式数据库的@Repository/@Service
集成测试。在我的测试类中,我想用一些数据预加载数据库。
我目前正在使用@BeforeEach
加载我的样本数据,但是,这段代码是在我的类中的每个测试中运行的。
在加载了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
。。。是否可以像加载spring应用程序上下文后那样以@BeforeAll
的方式加载它?
在Spring应用程序上下文加载后,有没有任何方法可以在测试数据中加载
基本上是的,我认为你可以做到:
其思想是在启动应用程序上下文时或在启动过程中加载SQL数据。
例如,弹簧靴与Flyway的集成就是这样工作的(创建并加载了Flyway的bean(。因此,从理论上讲,您可以将Flyway与测试迁移一起使用,测试迁移将包含测试数据生成的所有相关SQL脚本。
从技术上讲,你怎么能做到这一点?
这里有一种方法:
创建一个特殊的bean(就像它使用Flyway的方式一样(,它将取决于您的存储库,并在构建后保存数据:
@Component
public class SqlGenerationBean {
@Autowired
private MyRepository repo;
@PostConstruct
public void init() {
repo.save();
}
}
另一种方法是创建一个侦听器,该侦听器将在启动应用程序上下文时被调用,并再次调用相同的repo.save()
。
在这两种情况下,bean/listener代码都不应该从生产中访问(它只用于测试(:所以把它放在src/test/java
下的某个地方,例如
现在,一旦应用程序上下文启动,您就可以使用一个巧妙的技巧:
用@Transactional
注释标记您的测试。Spring将把代码封装在一个人工事务中,该事务将自动回滚(即使测试成功(,这样您将在测试期间修改的所有数据都将回滚,基本上在每次测试之前,您都将具有相同的状态(与应用程序上下文启动时/之后数据库的状态相同(。当然,如果在测试中使用DDL,有些数据库不能将其作为事务的一部分,但它实际上取决于数据库。
另一个有趣的地方是,应用程序上下文甚至可以在测试用例之间缓存(只创建一次(,所以请记住这一点。
在这种情况下,我只需要为测试类创建一个构造函数。它会在一切发生之前被触发。
@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
是注释中提到的最佳候选者