You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:57:18