测试含Autowired对象的Spring服务类遇空指针,求正确测试方法
问题原因与解决方案
你的测试代码中,虽然Mock了ExternalService,但并没有将这个Mock对象注入到MyService实例中,导致MyService里的externalService字段始终为null,触发空指针异常。以下是几种正确的写法:
方案一:使用@InjectMocks自动注入(快速解决)
Mockito的@InjectMocks注解会自动将标记为@Mock的对象注入到目标实例中,修改测试类如下:
@ExtendWith(MockitoExtension.class) public class MyServiceTest { // @Mock注解会自动创建ExternalService的Mock实例,无需手动调用Mockito.mock @Mock private ExternalService externalService; // @InjectMocks会将上面的Mock对象注入到MyService实例中 @InjectMocks private MyService myService; @Test public void methodToTest_Test() { // 注意:thenReturn不能用匹配器anyString(),要返回具体值 Mockito.when(externalService.call(Mockito.anyString())).thenReturn("模拟的响应内容"); // 调用待测试方法 String result = myService.methodToTest("测试参数"); // 验证ExternalService的call方法是否被调用 Mockito.verify(externalService).call("测试参数"); // 这里可以添加断言验证结果 // Assertions.assertEquals("预期返回值", result); } }
方案二:改用构造器注入(推荐的最佳实践)
Spring官方推荐使用构造器注入依赖,这种方式不仅让依赖关系更清晰,也能避免测试时的注入问题。先修改MyService:
@Service public class MyService { // 用final修饰确保依赖不可变 private final ExternalService externalService; // 构造器注入,Spring 4.3+可以省略@Autowired注解 public MyService(ExternalService externalService) { this.externalService = externalService; } public String methodToTest(String myArg) { String response = externalService.call(myArg); // ...业务逻辑 return response; } }
然后测试类可以直接手动传入Mock对象,无需依赖@InjectMocks:
@ExtendWith(MockitoExtension.class) public class MyServiceTest { @Mock private ExternalService externalService; private MyService myService; // 每个测试前初始化MyService实例 @BeforeEach void setUp() { myService = new MyService(externalService); } @Test public void methodToTest_Test() { Mockito.when(externalService.call("测试参数")).thenReturn("模拟响应"); String result = myService.methodToTest("测试参数"); Mockito.verify(externalService).call("测试参数"); // Assertions.assertEquals("模拟响应", result); } }
原代码的其他问题说明
- 使用
@Mock注解时,不需要手动调用Mockito.mock(ExternalService.class),注解会自动完成Mock实例的创建。 thenReturn(anyString())是错误用法:anyString()是参数匹配器,只能用于方法参数位置,返回值需要传入具体的字符串。
内容的提问来源于stack exchange,提问作者Lorenzo D'Isidoro
相关产品推荐
相关产品推荐

