如何在Controller的Mockito JUnit测试中Mock同一接口的两个实现类
我完全懂你碰到的这个痛点——当同一个接口有多个实现,还用@Qualifier区分注入到控制器里时,Mockito测试很容易因为Mock实例没正确绑定到对应的注入点而抛出空指针。下面我给你两种可行的解决方案,分别对应Spring Boot集成测试和纯Mockito单元测试场景:
先看控制器的示例代码(方便对照)
假设你的控制器是构造器注入的(这是Spring推荐的方式,也更利于测试):
@RestController @RequestMapping("/api/clients") public class ClientController { private final Client pClient; private final Client sClient; // 用@Qualifier指定要注入的实现类对应的bean名称 public ClientController(@Qualifier("pClient") Client pClient, @Qualifier("sClient") Client sClient) { this.pClient = pClient; this.sClient = sClient; } // 示例业务方法 @GetMapping("/test") public String getClientResults() { return pClient.process() + " | " + sClient.process(); } }
方案1:Spring Boot集成测试(用@MockBean)
如果是需要启动Spring上下文的集成测试,用@MockBean结合name属性来指定对应的Mock实例,这样Spring会自动把正确的Mock注入到控制器的对应位置:
@SpringBootTest @AutoConfigureMockMvc public class ClientControllerIntegrationTest { @Autowired private MockMvc mockMvc; // 这里的name要和控制器@Qualifier里的名称完全一致 @MockBean(name = "pClient") private Client pClientMock; @MockBean(name = "sClient") private Client sClientMock; @Test public void testGetClientResults() throws Exception { // 给两个Mock实例存根方法 when(pClientMock.process()).thenReturn("PClient 处理结果"); when(sClientMock.process()).thenReturn("SClient 处理结果"); // 发起请求并验证响应 mockMvc.perform(get("/api/clients/test")) .andExpect(status().isOk()) .andExpect(content().string("PClient 处理结果 | SClient 处理结果")); } }
方案2:纯Mockito单元测试(不用Spring上下文)
如果是轻量级的单元测试,用MockitoExtension配合@InjectMocks和带@Qualifier的@Mock,明确告诉Mockito哪个Mock对应控制器里的哪个依赖:
@ExtendWith(MockitoExtension.class) public class ClientControllerUnitTest { // 要测试的控制器实例,Mockito会自动注入依赖 @InjectMocks private ClientController clientController; // 用@Qualifier标注,和控制器里的注入点一一对应 @Mock @Qualifier("pClient") private Client pClientMock; @Mock @Qualifier("sClient") private Client sClientMock; @Test public void testGetClientResults() { // 存根方法 when(pClientMock.process()).thenReturn("Mocked PClient"); when(sClientMock.process()).thenReturn("Mocked SClient"); // 调用控制器方法并断言结果 String result = clientController.getClientResults(); assertThat(result).isEqualTo("Mocked PClient | Mocked SClient"); } }
为什么之前会抛空指针?
大概率是你没有给两个Mock实例绑定对应的@Qualifier标识,导致Mockito/Spring无法区分同类型的依赖,最终控制器里的pClient或sClient没有被注入Mock实例,还是null,调用方法时自然就抛出NPE了。
额外注意点
- 确保你的实现类在Spring容器中的bean名称和
@Qualifier指定的一致:默认情况下,Spring会把类名首字母小写作为bean名称(比如PClient类的bean名是pClient),如果自定义了bean名称(比如用@Bean(name = "customPClient")),那@Qualifier也要对应改成这个名称。 - 尽量用构造器注入,而不是字段注入:构造器注入更透明,测试时也更容易排查依赖注入的问题。
内容的提问来源于stack exchange,提问作者Harish
相关产品推荐
相关产品推荐

