对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

@MockitoBean@MockitoSpyBean

@MockitoBean@MockitoSpyBean 可用于测试类中,分别使用 Mockito 的 mockspy 来覆盖测试的 ApplicationContext 中的某个 Bean。在后一种情况下,原始 Bean 的早期实例会被捕获并由 spy 包装。spring-doc.cadn.net.cn

这些注解可以按以下方式应用。spring-doc.cadn.net.cn

  • 在测试类或其任意父类中的非静态字段上。spring-doc.cadn.net.cn

  • @Nested 测试类的外部类中的非静态字段上,或在 @Nested 测试类之上类型层次结构或外部类层次结构中的任意类中。spring-doc.cadn.net.cn

  • 在测试类的类型级别上,或在测试类之上的类型层次结构中的任何超类或所实现的接口上。spring-doc.cadn.net.cn

  • @Nested 测试类的外部类上,或在该 @Nested 测试类之上类型层次结构或外部类层次结构中的任意类或接口上,以类型级别进行标注。spring-doc.cadn.net.cn

当在字段上声明 @MockitoBean@MockitoSpyBean 时,要模拟(mock)或监视(spy)的 Bean 将根据被注解字段的类型进行推断。如果在 ApplicationContext 中存在多个候选 Bean,则可以在该字段上声明 @Qualifier 注解以帮助消除歧义。如果没有 @Qualifier 注解,将使用被注解字段的名称作为备用限定符(fallback qualifier)。另外,你也可以通过设置注解中的 valuename 属性,显式指定要模拟或监视的 Bean 名称。spring-doc.cadn.net.cn

当在类型级别声明 @MockitoBean@MockitoSpyBean 时,必须通过注解中的 types 属性提供要模拟(mock)或侦听(spy)的 Bean(或多个 Bean)的类型——例如 @MockitoBean(types = {OrderService.class, UserService.class})。如果 ApplicationContext 中存在多个候选者,您可以通过设置 name 属性显式指定要模拟或侦听的 Bean 名称。但请注意,如果配置了显式的 Bean name,则 types 属性必须仅包含单一类型——例如 @MockitoBean(name = "ps1", types = PrintingService.class)spring-doc.cadn.net.cn

为了支持模拟(mock)配置的复用,@MockitoBean@MockitoSpyBean 可用作元注解(meta-annotations),以创建自定义的组合注解(composed annotations)——例如,将通用的模拟或间谍(spy)配置定义在一个注解中,以便在整个测试套件中重复使用。@MockitoBean@MockitoSpyBean 也可以作为可重复注解(repeatable annotations)在类型级别上使用——例如,按名称模拟或监视多个 Bean。spring-doc.cadn.net.cn

限定符(包括字段的名称)用于确定是否需要创建一个独立的ApplicationContext。如果您在多个测试类中使用此功能来模拟(mock)或监视(spy)同一个 bean,请确保字段命名保持一致,以避免创建不必要的上下文。spring-doc.cadn.net.cn

@MockitoBean@MockitoSpyBean@ContextHierarchy 结合使用可能会导致不良结果,因为默认情况下每个 @MockitoBean@MockitoSpyBean 都会应用于所有上下文层级。为确保特定的 @MockitoBean@MockitoSpyBean 仅应用于单个上下文层级,请将 contextName 属性设置为匹配已配置的 @ContextConfiguration 名称——例如 @MockitoBean(contextName = "app-config")@MockitoSpyBean(contextName = "app-config")spring-doc.cadn.net.cn

有关更多详细信息和示例,请参见 带有 Bean 覆盖的上下文层次结构spring-doc.cadn.net.cn

每个注解还定义了特定于 Mockito 的属性,用于微调模拟行为。spring-doc.cadn.net.cn

@MockitoBean 注解使用 REPLACE_OR_CREATE bean 覆盖策略。 如果对应的 bean 不存在,将创建一个新的 bean。不过,你可以通过将 REPLACE 属性设置为 enforceOverride 来切换到 true 策略—— 例如:@MockitoBean(enforceOverride = true)spring-doc.cadn.net.cn

@MockitoSpyBean 注解使用 WRAP 策略, 并将原始实例包装在一个 Mockito spy 中。该策略要求必须恰好存在一个候选 bean。spring-doc.cadn.net.cn

正如 Mockito 文档中所述,有时使用 Mockito.when() 来对 spy 对象进行打桩(stubbing)是不合适的——例如,当在 spy 对象上调用真实方法会导致不良副作用时。spring-doc.cadn.net.cn

为避免此类不希望出现的副作用,请考虑使用 Mockito.doReturn(…​).when(spy)…​Mockito.doThrow(…​).when(spy)…​Mockito.doNothing().when(spy)…​ 以及类似的方法。spring-doc.cadn.net.cn

只有单例Bean可以被覆盖。任何尝试覆盖非单例Bean的行为都会导致异常。spring-doc.cadn.net.cn

当使用 @MockitoBean 来模拟由 FactoryBean 创建的 Bean 时,FactoryBean 将被替换为一个单例的模拟对象,其类型为该 FactoryBean 所创建的对象类型。spring-doc.cadn.net.cn

当使用 @MockitoSpyBeanFactoryBean 创建间谍(spy)时,所创建的间谍是针对 FactoryBean 所生成的对象,而不是 FactoryBean 本身。spring-doc.cadn.net.cn

@MockitoBean@MockitoSpyBean 字段的可见性没有任何限制。spring-doc.cadn.net.cn

因此,这些字段可以是 publicprotected、包私有(默认可见性)或 private,具体取决于项目的需求或编码规范。spring-doc.cadn.net.cn

@MockitoBean示例

以下示例展示了如何使用 @MockitoBean 注解的默认行为。spring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoBean (1)
	CustomService customService;

	// tests...
}
1 使用 Mockito mock 替换类型为 CustomService 的 bean。

在上面的示例中,我们正在为 CustomService 创建一个模拟对象。如果存在多个该类型的 Bean,则会考虑名为 customService 的 Bean。否则,测试将失败,您需要提供某种限定符来指明您想要覆盖哪一个 CustomService Bean。如果不存在这样的 Bean,系统将使用自动生成的 Bean 名称创建一个 Bean。spring-doc.cadn.net.cn

以下示例使用按名称查找,而不是按类型查找。如果不存在名为 service 的 bean,则会创建一个。spring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoBean("service") (1)
	CustomService customService;

	// tests...

}
1 将名为 service 的 bean 替换为一个 Mockito mock。

以下 @SharedMocks 注解按类型注册了两个模拟对象,按名称注册了一个模拟对象。spring-doc.cadn.net.cn

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = {OrderService.class, UserService.class}) (1)
@MockitoBean(name = "ps1", types = PrintingService.class) (2)
public @interface SharedMocks {
}
1 按类型注册 OrderServiceUserService 的模拟对象。
2 按名称注册 PrintingService 的模拟对象。

以下演示了如何在测试类上使用 @SharedMocksspring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
@SharedMocks (1)
class BeanOverrideTests {

	@Autowired OrderService orderService; (2)

	@Autowired UserService userService; (2)

	@Autowired PrintingService ps1; (2)

	// Inject other components that rely on the mocks.

	@Test
	void testThatDependsOnMocks() {
		// ...
	}
}
1 通过自定义的 @SharedMocks 注解注册通用的模拟对象。
2 可选地注入模拟对象以对其进行打桩验证
这些模拟对象也可以注入到 @Configuration 类或其他与测试相关的组件中,这些组件位于 ApplicationContext 中,以便使用 Mockito 的桩(stubbing)API 对它们进行配置。

@MockitoSpyBean示例

以下示例展示了如何使用 @MockitoSpyBean 注解的默认行为。spring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoSpyBean (1)
	CustomService customService;

	// tests...
}
1 使用 Mockito 的 spy 包装类型为 CustomService 的 bean。

在上面的示例中,我们正在包装类型为 CustomService 的 bean。如果存在多个该类型的 bean,则会使用名为 customService 的 bean。否则,测试将失败,您需要提供某种限定符(qualifier)来指明您想要监视(spy)哪一个 CustomService bean。spring-doc.cadn.net.cn

以下示例使用按名称查找,而不是按类型查找。spring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoSpyBean("service") (1)
	CustomService customService;

	// tests...
}
1 使用 Mockito spy 包装名为 service 的 bean。

以下 @SharedSpies 注解按类型注册了两个间谍(spy),并按名称注册了一个间谍(spy)。spring-doc.cadn.net.cn

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoSpyBean(types = {OrderService.class, UserService.class}) (1)
@MockitoSpyBean(name = "ps1", types = PrintingService.class) (2)
public @interface SharedSpies {
}
1 按类型注册 OrderServiceUserService 的间谍(spies)。
2 按名称注册 PrintingService 的间谍(spy)。

以下演示了如何在测试类上使用 @SharedSpiesspring-doc.cadn.net.cn

@SpringJUnitConfig(TestConfig.class)
@SharedSpies (1)
class BeanOverrideTests {

	@Autowired OrderService orderService; (2)

	@Autowired UserService userService; (2)

	@Autowired PrintingService ps1; (2)

	// Inject other components that rely on the spies.

	@Test
	void testThatDependsOnMocks() {
		// ...
	}
}
1 通过自定义的 @SharedSpies 注解注册通用的间谍(spies)。
2 可选地注入间谍(spies)以对其进行打桩(stub)或验证(verify)。
这些间谍(spies)也可以被注入到 @Configuration 类或 ApplicationContext 中的其他测试相关组件中,以便使用 Mockito 的桩(stubbing)API 对它们进行配置。