@MockitoBean@MockitoSpyBean

@MockitoBean@MockitoSpyBean 可用于测试类中,以使用 Mockito 的 mockspy 分别覆盖测试的 ApplicationContext 中的 bean。在后一种情况下,原始 bean 的早期实例被捕获并由 spy 包装。 这些注解可以通过以下方式应用。

  • 在测试类或其任何超类中的非静态字段上。

  • @Nested 测试类的封闭类中的非静态字段上,或在 @Nested 测试类上方的类型层次结构或封闭类层次结构中的任何类中。

  • 在测试类或其上方类型层次结构中的任何超类或已实现的接口的类型级别上。

  • @Nested 测试类的封闭类上,或在 @Nested 测试类上方的类型层次结构或封闭类层次结构中的任何类或接口的类型级别上。

@MockitoBean@MockitoSpyBean 在字段上声明时,要模拟或监视的 bean 从带注解字段的类型推断。如果 ApplicationContext 中存在多个候选对象,可以在字段上声明 @Qualifier 注解以帮助消除歧义。在没有 @Qualifier 注解的情况下,带注解字段的名称将用作 回退限定符。或者,您可以通过设置注解中的 valuename 属性来显式指定要模拟或监视的 bean 名称。 当 @MockitoBean@MockitoSpyBean 在类型级别声明时,要模拟或监视的 bean 类型(或多个 bean)必须通过注解中的 types 属性提供——例如,@MockitoBean(types = {OrderService.class, UserService.class})。如果 ApplicationContext 中存在多个候选对象,您可以通过设置 name 属性来显式指定要模拟或监视的 bean 名称。但请注意,如果配置了显式 bean name,则 types 属性必须包含单个类型——例如,@MockitoBean(name = "ps1", types = PrintingService.class)。 为了支持 mock 配置的重用,@MockitoBean@MockitoSpyBean 可以用作元注解来创建自定义的 组合注解——例如,在一个注解中定义通用的 mock 或 spy 配置,以便在整个测试套件中重用。@MockitoBean@MockitoSpyBean 也可以作为可重复注解在类型级别使用——例如,按名称模拟或监视多个 bean。

限定符,包括字段的名称,用于确定是否需要创建单独的 ApplicationContext。如果您使用此功能在多个测试类中模拟或监视同一个 bean,请确保一致地命名这些字段,以避免创建不必要的上下文。

@MockitoBean@MockitoSpyBean@ContextHierarchy 结合使用可能会导致不良结果,因为每个 @MockitoBean@MockitoSpyBean 默认将应用于所有上下文层次结构级别。为确保特定的 @MockitoBean@MockitoSpyBean 仅应用于单个上下文层次结构级别,请将 contextName 属性设置为与配置的 @ContextConfiguration 名称匹配——例如,@MockitoBean(contextName = "app-config")@MockitoSpyBean(contextName = "app-config")。 有关更多详细信息和示例,请参阅 带 bean 覆盖的上下文层次结构

每个注解还定义了 Mockito 特定的属性来微调模拟行为。 @MockitoBean 注解使用 REPLACE_OR_CREATE bean 覆盖策略。 如果不存在相应的 bean,则会创建一个新 bean。但是,您可以通过将 enforceOverride 属性设置为 true 来切换到 REPLACE 策略——例如,@MockitoBean(enforceOverride = true)@MockitoSpyBean 注解使用 WRAP 策略, 并且原始实例被包装在 Mockito spy 中。此策略要求恰好存在一个候选 bean。

如 Mockito 文档中所述,有时使用 Mockito.when() 不适合用于桩化 spy——例如,如果调用 spy 上的真实方法导致不期望的副作用。 为了避免这种不期望的副作用,请考虑使用 Mockito.doReturn(…​).when(spy)…​Mockito.doThrow(…​).when(spy)…​Mockito.doNothing().when(spy)…​ 和类似的方法。

只有 单例 bean 可以被覆盖。任何覆盖非单例 bean 的尝试都将导致异常。 当使用 @MockitoBean 模拟由 FactoryBean 创建的 bean 时,FactoryBean 将被替换为 FactoryBean 创建的对象类型的单例 mock。 当使用 @MockitoSpyBeanFactoryBean 创建 spy 时,将为 FactoryBean 创建的对象创建 spy,而不是为 FactoryBean 本身创建 spy。

@MockitoBean@MockitoSpyBean 字段的可见性没有限制。 因此,此类字段可以是 publicprotected、包私有(默认可见性)或 private,具体取决于项目的需求或编码实践。

@MockitoBean 示例

以下示例展示了如何使用 @MockitoBean 注解的默认行为。

Java
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoBean [id="CO1-1"][id="CO1-1"][id="CO1-1"](1)
	CustomService customService;

	// tests...
}
<1>  将类型为 `CustomService` 的 bean 替换为 Mockito mock。

在上面的示例中,我们正在为 CustomService 创建一个 mock。如果存在多个该类型的 bean,则考虑名为 customService 的 bean。否则,测试将失败,您需要提供某种限定符来标识您要覆盖哪个 CustomService bean。如果不存在这样的 bean,将创建一个具有自动生成 bean 名称的 bean。

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

Java
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoBean("service") [id="CO2-1"][id="CO1-2"][id="CO2-1"](1)
	CustomService customService;

	// tests...

}
<1>  将名为 `service` 的 bean 替换为 Mockito mock。

以下 @SharedMocks 注解按类型注册了两个 mock,按名称注册了一个 mock。

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoBean(types = {OrderService.class, UserService.class}) [id="CO3-1"][id="CO1-3"][id="CO3-1"](1)
@MockitoBean(name = "ps1", types = PrintingService.class) [id="CO3-2"][id="CO1-4"][id="CO3-2"](2)
public @interface SharedMocks {
}
<1>  按类型注册 `OrderService` 和 `UserService` mock。
<1>  按名称注册 `PrintingService` mock。

以下示例演示了如何在测试类上使用 @SharedMocks

Java
@SpringJUnitConfig(TestConfig.class)
@SharedMocks [id="CO4-1"][id="CO1-5"][id="CO4-1"](1)
class BeanOverrideTests {

	@Autowired OrderService orderService; [id="CO4-2"][id="CO1-6"][id="CO4-2"](2)

	@Autowired UserService userService; [id="CO4-3"][id="CO1-7"][id="CO4-3"](2)

	@Autowired PrintingService ps1; [id="CO4-4"][id="CO1-8"][id="CO4-4"](2)

	// Inject other components that rely on the mocks.

	@Test
	void testThatDependsOnMocks() {
		// ...
	}
}
<1>  通过自定义 `@SharedMocks` 注解注册通用 mock。
<1>  可选注入 mock 以 _桩化_ 或 _验证_ 它们。

这些 mock 也可以注入到 @Configuration 类或 ApplicationContext 中的其他与测试相关的组件中,以便使用 Mockito 的桩化 API 配置它们。

@MockitoSpyBean 示例

以下示例展示了如何使用 @MockitoSpyBean 注解的默认行为。

Java
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoSpyBean [id="CO5-1"][id="CO1-9"][id="CO5-1"](1)
	CustomService customService;

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

在上面的示例中,我们正在包装类型为 CustomService 的 bean。如果存在多个该类型的 bean,则考虑名为 customService 的 bean。否则,测试将失败,您需要提供某种限定符来标识您要监视哪个 CustomService bean。

以下示例使用按名称查找,而不是按类型查找。

Java
@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoSpyBean("service") [id="CO6-1"][id="CO1-10"][id="CO6-1"](1)
	CustomService customService;

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

以下 @SharedSpies 注解按类型注册了两个 spy,按名称注册了一个 spy。

Java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@MockitoSpyBean(types = {OrderService.class, UserService.class}) [id="CO7-1"][id="CO1-11"][id="CO7-1"](1)
@MockitoSpyBean(name = "ps1", types = PrintingService.class) [id="CO7-2"][id="CO1-12"][id="CO7-2"](2)
public @interface SharedSpies {
}
<1>  按类型注册 `OrderService` 和 `UserService` spy。
<1>  按名称注册 `PrintingService` spy。

以下示例演示了如何在测试类上使用 @SharedSpies

Java
@SpringJUnitConfig(TestConfig.class)
@SharedSpies [id="CO8-1"][id="CO1-13"][id="CO8-1"](1)
class BeanOverrideTests {

	@Autowired OrderService orderService; [id="CO8-2"][id="CO1-14"][id="CO8-2"](2)

	@Autowired UserService userService; [id="CO8-3"][id="CO1-15"][id="CO8-3"](2)

	@Autowired PrintingService ps1; [id="CO8-4"][id="CO1-16"][id="CO8-4"](2)

	// Inject other components that rely on the spies.

	@Test
	void testThatDependsOnMocks() {
		// ...
	}
}
<1>  通过自定义 `@SharedSpies` 注解注册通用 spy。
<1>  可选注入 spy 以 _桩化_ 或 _验证_ 它们。

这些 spy 也可以注入到 @Configuration 类或 ApplicationContext 中的其他与测试相关的组件中,以便使用 Mockito 的桩化 API 配置它们。