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

@MockitoBean@MockitoSpyBean

@MockitoBean@MockitoSpyBean 可以在测试类中使用,以分别用Mockito的模拟对象间谍对象覆盖测试的ApplicationContext中的bean。在后一种情况下,原始bean的早期实例将被捕获并由间谍对象包裹。spring-doc.cadn.net.cn

注解可以通过以下方式应用。spring-doc.cadn.net.cn

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

  • 在一个非静态字段中,该字段位于封闭类中,用于一个测试类0,或在类型层次结构或封闭类层次结构中位于测试类1之上的任何类中。spring-doc.cadn.net.cn

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

  • 在包围@Nested测试类的类的类型级别上,或在@Nested测试类上方的类型层次结构或包围类层次结构中的任何类或接口上。spring-doc.cadn.net.cn

当在字段上声明@MockitoBean@MockitoSpyBean时,要模拟或监视的bean将从注解字段的类型中推断。如果在ApplicationContext中存在多个候选者,可以在字段上声明一个@Qualifier注解来帮助消除歧义。在没有@Qualifier注解的情况下,将使用注解字段的名称作为回退限定符。或者,您可以通过在注解中设置valuename属性来显式指定要模拟或监视的bean名称。spring-doc.cadn.net.cn

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

为了支持模拟配置的重用,@MockitoBean@MockitoSpyBean 可以作为元注解来创建自定义的组合注解——例如,为了在一个注解中定义通用的模拟或间谍配置,该注解可以在整个测试套件中重复使用。@MockitoBean@MockitoSpyBean 也可以作为类型级别的可重复注解使用——例如,通过名称模拟或监视多个bean。spring-doc.cadn.net.cn

限定符,包括字段的名称,用于确定是否需要创建独立的 ApplicationContext。如果您使用此功能在多个测试类中模拟或监视相同的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。但是,您可以通过 将enforceOverride属性设置为true来切换到REPLACE策略—— 例如,@MockitoBean(enforceOverride = true)spring-doc.cadn.net.cn

@MockitoSpyBean 注解使用了 WRAP 策略, 原始实例被包裹在 Mockito 模拟对象中。此策略要求存在 恰好一个候选bean。spring-doc.cadn.net.cn

根据Mockito文档所述,有时使用Mockito.when()来为spy模拟方法调用是不合适的——例如,如果在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 创建间谍时,将为 FactoryBean 创建的对象创建间谍,而不是为 FactoryBean 本身创建间谍。spring-doc.cadn.net.cn

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

因此,这些字段可以是 publicprotected、包私有(默认可见性), 或根据项目的需要或编码规范为 privatespring-doc.cadn.net.cn

@MockitoBean 示例

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

@SpringJUnitConfig(TestConfig.class)
class BeanOverrideTests {

	@MockitoBean (1)
	CustomService customService;

	// tests...
}
1 将类型为CustomService的bean替换为Mockito模拟对象。

在上述示例中,我们为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模拟对象。

以下 @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 通过自定义的@MockBean注解注册通用的模拟对象。
2 可选择性地注入模拟对象以存根验证它们。
模拟对象也可以被注入到@Configuration类或其他测试相关的 组件中,在ApplicationContext中,以便使用Mockito的存根API进行配置。

@MockitoSpyBean 示例

以下示例展示了如何使用@0注解的默认行为。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。否则,测试将失败,您需要提供某种限定符来标识您想要监视的是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 注解通过类型注册了两个间谍对象和一个通过名称注册的间谍对象。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 个间谍。
2 通过名称注册 PrintingService 个间谍。

以下演示了如何在测试类中使用@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 通过自定义的@Spy注解注册通用间谍。
2 可选择性地注入模拟对象以存根验证它们。
间谍同样可以被注入到@Configuration类或其他与测试相关的 组件中,在ApplicationContext里,以便使用Mockito的存根API进行配置。