spring - 在测试类之外使用 Spring Boot/JUnit 5 连接到测试生命周期事件
问题描述
JUnit 5 具有@BeforeEach
, @BeforeAll
, @AfterAll
,@AfterEach
注释等功能,允许您在测试生命周期事件期间执行代码,例如清除测试之间共享的状态。
我想要一种为某些测试配置自动执行此操作的方法,而不必记住将这些生命周期挂钩放在每个测试中。
例如:
如果我有一个加载一些存根数据源的 Spring 测试配置:
interface DataProvider {
String getData(String id);
}
还有一个依赖于此的类:
@Component
class ClassThatDependsOnDataProvider {
private final DataProvider dataProvider;
@Autowired
ClassThatDependsOnDataProvider(DataProvider dataProvider) {
this.dataProvider = dataProvider;
}
public String process(String id) {
String data = dataProvider.get(id);
return some_processing_of(data);
}
}
通常这可能会遇到后端,但对于我的测试,我将使用内存解决方案将其换掉:
@TestComponent
class InMemoryDataProvider implements DataProvider {
private Map<String, String> data = new HashMap<>();
public void setData(String id, String data) {
data.put(id, data);
}
public String getData(String id) {
return data.get(id);
}
public void clear() {
data.clear();
}
}
在我的测试中,我可能想用它来存根后端的数据,例如:
@SpringBootTest
@Import({InMemoryDataProvider.class})
class ClassThatDependsOnDataProviderTests {
@Autowired InMemoryDataProvider dataProvider;
@Autowired ClassThatDependsOnDataProvider classThatDependsOnDataProvider;
@Test
public void should_correctly_process_data() {
// given i have an id with a value
dataProvider.setData("stub_id", "stub_value");
// when i process my id
var result = classThatDependsOnDataProvider.process("stub_id");
// then i receive expected result
assertThat(result).isWhatIExpect();
}
// more @Tests
// DON'T WANT TO HAVE TO DO THIS IN EACH TEST CLASS
@AfterEach
public void tearDown() {
dataProvider.clear();
}
}
我希望能够删除tearDown()
这里用 注释的方法@AfterEach
,并将该行为移动到测试组件/测试假或配置类中,这样,使用一组这样的提供程序,很容易维护一组的测试将用假版本替换它们,而无需为每个测试手动管理其生命周期。
是否有某种 Spring 功能、生命周期事件或 JUnit 5 功能允许这样做?
请原谅这个简单的例子,显然对于这么简单的测试来说太过分了,但这只是讨论的参考。假设@MockBean
或使用模拟库不是一种选择。
解决方案
您可以使用 JUnit5 扩展:https ://junit.org/junit5/docs/current/user-guide/#extensions 。
例如,您的扩展将类似于以下内容:
public class InMemoryDataProviderExtension implements AfterEachCallback {
/**
* {@inheritDoc}
*/
@Override
public void afterEach(final ExtensionContext context) throws Exception {
final ApplicationContext applicationContext = SpringExtension.getApplicationContext(context);
final InMemoryDataProvider inMemoryDataProvider = applicationContext.getBean(InMemoryDataProvider.class);
inMemoryDataProvider.clear();
}
}
注意:
SpringExtension.getApplicationContext(context);
获取 spring 应用程序上下文,因此您可以检索上下文中定义的 bean- 该扩展实现了 AfterEachCallback 接口,但其他扩展点存在其他接口(即 BeforeEachCallback、BeforeAllCallback 等)
为了在您的测试中激活扩展:
@SpringBootTest
@ExtendWith(InMemoryDataProviderExtension.class)
public class ClassThatDependsOnDataProviderTests { ... }
推荐阅读
- angular - 如果加载选项需要时间,则绑定选择的正确方法
- html - 如何使用 CSS 在两个图像之间创建剪辑路径
- html - 如何在 Angular 11 中使用 Jquery ligtslider
- reactjs - 通过 id React + Flask 获取数据
- javascript - setInterval vs setTimeout vs requestAnimationFrame
- sql - JPQL 中的嵌套查询
- sqlite - 用于数据采集的 SQLite 与 CSV
- backend - getRestrictions()->removeByType 不删除限制
- python - 编译成exe后如何将py程序编码为不被检测为病毒?
- assembly - 为什么退出代码是什么以及为什么调用的第一个字节是 0x0000 然后是“printf”