Spring Boot MockMvc测试:when()thenReturn()存根返回Null问题求助
问题分析与修复方案
我来帮你排查下为什么Mockito的Stub没生效返回null的问题,主要有两个核心原因,对应两种修复方式:
1. 最直接的原因:参数不匹配
你看控制器里调用服务层的时候,对source和target做了toUpperCase()转换:
SendingResponseDTO val = converterService.convertService(source.toUpperCase(), target.toUpperCase(), number);
但你的测试代码里,Stub用的是原字符串(哪怕是大写,万一测试用例里是小写就会匹配失败):
Mockito.when(cs.convertService(source, target, number)).thenReturn(dto);
Mockito的参数匹配是严格相等匹配,只要参数不完全一致,就会返回null。所以你只需要把Stub里的参数也转成大写,和控制器的调用保持一致就行:
Mockito.when(cs.convertService(source.toUpperCase(), target.toUpperCase(), number)).thenReturn(dto);
2. 更规范的测试姿势:修正测试注解的使用
你的测试类用了@SpringBootTest,这个注解会启动完整的Spring应用上下文,这时候你用@InjectMocks和@Mock注入的对象,很可能会被Spring自动装配的真实服务bean覆盖,导致Mock根本没生效。
对于Spring MVC控制器的单元测试,推荐用@WebMvcTest,它只会加载控制器相关的组件,配合@MockBean来模拟依赖的服务层,既轻量又准确。修改后的测试类如下:
@RunWith(SpringRunner.class) @WebMvcTest(CurrencyConverterController.class) public class CurrencyConverterControllerTest { @Autowired private MockMvc mockMvc; @MockBean private CurrencyConverterService cs; @Test public void statusCheckTest() throws Exception { SendingResponseDTO dto = new SendingResponseDTO(); String source = "GBP"; String target = "USD"; Double number = 32.56; dto.setSourceCurrency(source); dto.setTargetCurrency(target); dto.setNumber(number); dto.setTargetCurrencyValue(1.3136300664); dto.setTotalValue("$42.77"); String apiURL = "/currency/converter/" + source + "/" + target + "/" + number; // 匹配控制器中实际调用的参数(转大写) Mockito.when(cs.convertService(source.toUpperCase(), target.toUpperCase(), number)).thenReturn(dto); mockMvc.perform(get(apiURL).contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andDo(print()) // 额外添加断言验证返回内容,让测试更严谨 .andExpect(jsonPath("$.totalValue").value("$42.77")); } }
这样修改后,Spring会自动把Mock的服务注入到控制器中,参数匹配也正确,就能得到预期的DTO了。
小建议
如果你的测试只需要验证控制器的逻辑,用@WebMvcTest足够了,不需要启动完整的应用上下文,测试速度会快很多。另外,添加对返回JSON内容的断言,可以确保接口返回的结果符合预期,而不仅仅是状态码正确。
内容的提问来源于stack exchange,提问作者Syam Kumar
相关产品推荐
相关产品推荐

