领先一步
VMware 提供培训和认证,为您的发展注入动力。
了解更多Spring Framework 6.2.0-M1
已发布,其中包含解决一百多个问题的更改。这些更改中包括 Spring 测试支持中的一系列新特性。
在这篇文章中,我想向大家介绍这些新测试特性中的一个:Bean 覆盖支持。
使用 Spring TestContext Framework,您可以通过注解驱动的模型轻松地在集成测试中验证 Spring 应用程序的正确连线。
在单元测试中,依赖注入和 Spring 设计原则使您的代码对容器的依赖性降低,并且更容易手动桩或模拟组件的依赖项以便进行隔离测试。在集成测试中,这一点不太相关,因为测试旨在覆盖组件的正确连线。尽管您可能会发现在集成测试中需要替换 bean 的情况。
Spring Framework 团队通常不建议重新定义 bean。尽管在 `BeanDefinitionRegistry` 的默认实现中通过一个标志目前可以实现,但我们计划弃用它,并且 Spring Boot 已经通过默认关闭 bean 覆盖来选择退出。
然而,这更多是生产代码中的担忧,我们承认在测试中覆盖 bean 是有用且合理的。因此,我们旨在为该领域常见的覆盖场景提供一流的安排。
在 Spring Framework `6.2.0-M1` 中,我们引入了一个可扩展的 bean 覆盖特性,它允许您在集成测试中精确且明确地替换一个或多个 bean 定义,同时防止生产代码或测试的其他部分出现此类意外更改。
@TestBean
进行简单的基于方法的覆盖Spring TestContext Framework 现在提供了一个简单的 Bean 覆盖支持实现:`@TestBean` 注解。
覆盖名为 `example` 的 bean 分三步完成:添加一个与 bean 同名的字段,用 `@TestBean` 注解它,并添加一个名为 `exampleTestOverride` 的无参数 `static` 工厂方法。在该工厂方法中,如果 bean 类型是接口,您可以返回一个简化的实现,如下例所示:
@Configuration
class ProdConfiguration {
@Bean
MyService customService() {
return new ProdServiceImpl();
}
}
@SpringJUnitConfig
class MyServiceIntegrationTests {
@TestBean
MyService customService;
static MyService customServiceTestOverride() {
return new SimplifiedServiceImpl();
}
@Test
void test(ApplicationContext context) {
assertThat(context.getBean("customService")
.isSameAs(this.customService)
.isInstanceOf(SimplifiedServiceImpl.class);
//...
}
}
除非为 `@TestBean` 注解提供了 `beanName` 属性,否则带注解字段的名称将被解释为目标 bean 的名称。
`methodName` 参数也可用于指向不遵循默认命名约定 `{beanName}TestOverride` 的工厂方法。
Bean 覆盖机制负责解析此注解并替换注册表中的现有 bean 定义。测试类中的 `customService` 字段也会注入由 `customServiceTestOverride` 工厂方法生成的覆盖实例。
@MockitoBean
和 @MockitoSpyBean
进行基于 Mockito 的覆盖这第二个 bean 覆盖实现基于 Mockito 库。它带有两个注解:@MockitoBean
用于自动将目标单例 bean 替换为 mock,以及 @MockitoSpyBean
用于将 bean 包装在 spy 中。
这些注解都包含 Mockito 特有的属性,以便进一步配置目标 bean 如何进行 mocking。这包括支持指定 mock 在测试之间如何重置,如下例所示:
@Configuration
class ProdConfiguration {
@Bean
MyService customService() {
return new ProdService();
}
}
@SpringJUnitConfig
class MyServiceIntegrationTests {
@MockitoSpyBean(reset = MockReset.NONE)
MyService customService;
@Test
void test() {
//...
}
}
在上面的示例中,spy 在测试之间将不会重置。默认情况下,mock 和 spy 会在测试方法运行后重置。
请注意,要对 bean 进行 spy,首先必须存在被 spy 类的一个实际实例。Bean 覆盖特性支持这种特殊情况,允许在 bean 实例化后从元数据创建覆盖,此外还支持更常见的替换 bean 定义的情况。
新的测试中的 Bean 覆盖支持采用基于注解的模型,适用于您测试类中的字段。它是可扩展和可定制的,上面介绍的 3 个注解只是我们提供的默认实现。
实现您自己的 Bean 覆盖方式就像实现以下内容一样简单:
Spring TestContext Framework 解析测试类,查找任何用 `@BeanOverride` 元注解标记的字段,并实例化相关的 `BeanOverrideProcessor` 以注册一个 `OverrideMetadata` 实例。
然后 `BeanFactoryPostProcessor` 将使用该信息修改上下文,根据每个元数据注册和替换 bean 定义。
Spring TestContext Framework 现在提供了两种在测试中覆盖 bean 的方法,而没有意外副作用的风险。bean 覆盖机制是可扩展的,如果您更喜欢使用 Mockito 以外的 mocking 库,这将非常方便。
我们期待社区对这项功能的反馈,包括对第一版改进的建议。
与此同时,编程愉快!