Mockito实现方法免执行返回指定值(checkEnvironment桩不生效)
Spring单元测试Mockito桩配置不生效问题解决方案
问题描述
使用Mockito开展Spring单元测试时,连续2天尝试各类方案,桩配置始终无法生效:
- 核心诉求:
checkEnvironment方法无需执行内部逻辑,直接返回指定的true值 - 实际问题:被测方法始终无法返回预期的
true值
现有问题代码(可忽略编译错误)
/* 原英文注释翻译:尝试了各类方案,2天来`checkEnvironment`方法始终无法返回预期的true值,要求方法不执行内部逻辑直接返回指定值,可忽略代码中的编译错误 */ @RunWith(SpringJUnit4Runner.class) @WebAppConfiguration @ContextConfiguration(classes = { webconfix.clss }) public class LoginControllerTest { @Autowired private WebApplicationContext webcontext; @Mock LoginServiceImpl serviceimpl; @InjectMocks LoginController loginController; private MockMvc mockmvc; ObjectMapper objMapper = new ObjectMapper(); @Before public void Setup() throws Exception { MockitoAnnotations.initMocks(this); // 原代码存在拼写错误、重复初始化问题 mockMvc = MockMvcBuilders.standalonSetup(loginControllerTest).build; mockMvc = MockMvcBuilders.WebAppContextSetup(webcontext).build; } @Test public void loginTest() throws Exception { // 原代码单独生成spy对象,和Controller注入的依赖完全无关 LoginServiceImpl lgservice = spy(new LoginServiceImpl()); Login lg = new Login("test"); String requestData = objMapper.writeValueAsString(login); // 给未被Controller使用的对象配置桩,完全无效 Mockito.doReturn(Boolean.TRUE).when(lgservice).checkEnvironment("abc"); // Mockito.when(lgservice.checkEnvironment("abc")).thenReturn(Boolean.TRUE); MvcResult result = mockMvc.perform( MockMvcBuilders.post("/login") .content(requestdata) .contentType(MediaType.APPLICATION_JSON) .andExpect(status().isOk()) ).andReturn(); } }
核心问题排查
- Mock对象不匹配:类内通过
@Mock创建的serviceimpl才是被@InjectMocks注入到LoginController的依赖,但测试方法内单独spy(new LoginServiceImpl())生成了一个全新的独立对象,给这个对象配置桩,Controller完全感知不到,桩配置必然无效。 - MockMvc初始化逻辑冲突:先后调用两种不同的MockMvc初始化方式,后执行的
WebAppContextSetup会加载Spring容器内真实的LoginServiceImplBean,完全覆盖你配置的Mock对象,导致方法走真实逻辑执行。 - 参数匹配失效:即使使用正确的Mock对象,给
checkEnvironment("abc")配置桩,如果Controller实际调用时传入的参数不是固定字符串"abc",桩也不会触发返回。 - 附带编译错误:方法名拼写、变量名不一致、缺少括号等问题会直接导致运行异常。
解决方案
- 统一使用同一Mock对象:删除测试方法内单独创建的
spy对象,直接给类内@Mock注解的serviceimpl配置桩。 - 统一MockMvc初始化方式:纯单元测试无需依赖Spring容器,使用
standaloneSetup初始化即可,避免加载Web上下文和真实Bean;如果必须使用Web上下文,需手动把Mock对象注入Spring上下文,替换真实实现。 - 适配桩参数匹配规则:如果实际调用参数不确定,使用Mockito参数匹配器替代固定字符串。
修复后参考代码(纯单元测试场景,无需启动Spring容器)
@RunWith(MockitoJUnitRunner.class) // 纯单测直接用Mockito运行器,无需加载Spring上下文 public class LoginControllerTest { // 直接Mock依赖的Service @Mock private LoginServiceImpl serviceimpl; // 被测Controller,自动注入上方的Mock对象 @InjectMocks private LoginController loginController; private MockMvc mockMvc; private ObjectMapper objMapper = new ObjectMapper(); @Before public void setup() { // 初始化Mock,使用MockitoJUnitRunner运行器时可省略该行 MockitoAnnotations.initMocks(this); // 用standalone方式初始化MockMvc,和Controller的Mock依赖完全匹配 mockMvc = MockMvcBuilders.standaloneSetup(loginController).build(); } @Test public void loginTest() throws Exception { Login lg = new Login("test"); String requestData = objMapper.writeValueAsString(lg); // 直接给Mock的serviceimpl配置桩,匹配任意字符串入参都返回true Mockito.doReturn(true).when(serviceimpl).checkEnvironment(Mockito.anyString()); // 若确定实际入参固定,也可直接写对应参数 // Mockito.doReturn(true).when(serviceimpl).checkEnvironment("abc"); mockMvc.perform( MockMvcRequestBuilders.post("/login") .content(requestData) .contentType(MediaType.APPLICATION_JSON) ).andExpect(status().isOk()); } }
补充注意事项
- 如果需要保留
@WebAppConfiguration的Web上下文测试场景,需在测试类内添加@MockBean注解,替换Spring容器内的真实LoginServiceImplBean,否则容器始终会加载真实实现,Mock不生效。 - 桩配置必须在MockMvc发起请求前完成,否则请求执行时桩还未生效。
内容的提问来源于stack exchange,提问作者sam11
相关产品推荐
相关产品推荐

