本文介绍在不启动 spring 容器的前提下,使用 mockito 对依赖 `@autowired` 外部服务的 spring service 进行单元测试的正确方法,重点解决因未手动注入 mock 导致的空指针异常问题。
在 Spring 应用中,@Autowired 字段注入仅在 Spring IoC 容器管理 Bean 的生命周期时生效(例如通过 @SpringBootTest 启动上下文)。而在纯单元测试(如使用 @ExtendWith(MockitoExtension.class))中,Spring 容器并未参与对象创建,因此 MyService 实例中的 externalService 字段保持为 null,直接调用会触发 NullPointerExcept
ion。
正确的做法是:跳过 Spring 依赖注入机制,改用手动赋值方式将 Mock 对象注入目标 Service 实例。注意以下关键点:
以下是修正后的完整示例代码:
@ExtendWith(MockitoExtension.class)
class MyServiceTest {
@Mock
private ExternalService externalService;
private MyService myService; // 不要在此处初始化
@BeforeEach
void setUp() {
myService = new MyService(); // 创建真实实例
myService.externalService = externalService; // 手动注入 Mock
}
@Test
void methodToTest_Test() {
// 定义 Mock 行为:避免使用 anyString() 作为返回值
when(externalService.call("input")).thenReturn("mocked-response");
// 执行被测方法
String result = myService.methodToTest("input");
// 断言
assertThat(result).isEqualTo("mocked-response");
verify(externalService).call("input");
}
}⚠️ 注意事项:
总结:脱离 Spring 上下文的单元测试,本质是“普通 Java 对象测试”,所有依赖必须显式提供。手动注入 Mock 是最直接、可靠且易于理解的方式,兼顾可读性与可维护性。