MockMvc测试失败:Controller工具方法Mock失效引发空指针异常
解决MockMvc中工具方法Mock配置不生效的问题
我在Mock一个包含工具方法的Controller时遇到了麻烦:明明已经Mock了工具类的getOperatorsAdNameWrapper方法,但MockMvc执行请求时完全忽略了我用when(...)配置的返回结果,反而用空参数(""和null)调用这个方法,直接导致了空指针异常。请问怎么才能让我的Mock配置和mockMvc.perform()协同生效?
相关代码
Controller代码
@GetMapping(value = {"/wellbore"}) public String wellboreForm(Model model, @RequestParam("mode") String mode, HttpServletRequest request) { Set<String> operators = new LinkedHashSet<>(); String userName = (String) request.getSession().getAttribute("userName"); Set<String> operatorsSet = (HashSet<String>) request.getSession().getAttribute("userRoles"); Set<String> operatorsAdName = util.getOperatorsAdNameWrapper(userName, operatorsSet); operatorsAdName.forEach(adName -> { Query query = new Query() .setClassname(Wellbore.CLASS) .eq(Wellbore.operatorsGroup, adName); operators.addAll(getWellboresNameList(query)); }); model.addAttribute("wellboreDataList", operators); model.addAttribute("wellboreData", new WellboreForm()); return "ui/selectWellbore"; } public static Set<String> getOperatorsAdName(String userName, Set<String> operatorsAdName) { operatorsAdName.removeIf(x -> x.equals(userName) || x.equals("SCOUT") || x.equals("GTO") || x.equals("KADME") || x.equals("offline_access") || x.equals("uma_authorization")); return operatorsAdName; } public Set<String> getOperatorsAdNameWrapper(String userName, Set<String> operatorsAdName) { return getOperatorsAdName(userName,operatorsAdName); }
测试代码
@Mock private Util utilMock; @Test @DisplayName("GET /wellbore - Select Wellbore") void testMockMvc() throws Exception { HttpServletRequest req = Mockito.mock(HttpServletRequest.class); when(req.getAttribute("userName")).thenReturn("abcd"); String userName = (String) req.getAttribute("userName"); //Here I get the correct result when(utilMock.getOperatorsAdNameWrapper(userName, adNames)).thenReturn(roleSet); //another call made here with empty parameters to utilMock.getOperatorsAdNameWrapper("", null) mockMvc.perform(get("/wellbore").param("mode","selectWellbore") .sessionAttr("wellboreDataList", new LinkedHashSet<>()) .sessionAttr("wellboreData", new WellboreForm()) ) .andExpect(status().isOk()) .andExpect(view().name("ui/selectWellbore")) .andExpect(model().attribute("wellboreDataList", hasSize(2))); }
问题分析
你的Mock配置不生效主要有三个核心原因:
- Controller中的Util实例未被Mock替换:如果Controller里的
util是直接实例化(比如private Util util = new Util();)而不是依赖注入,那么你Mock的utilMock根本不会被Controller使用。 - Mock的HttpServletRequest未被MockMvc使用:你手动Mock了
HttpServletRequest,但MockMvc在执行请求时会创建自己的请求对象,这个Mock实例并没有被关联到请求中。 - Session属性设置错误:Controller从Session中获取
userName和userRoles,但你的测试只设置了wellboreDataList和wellboreData,导致这两个关键属性为null/空字符串,进而触发了空参数调用。
解决方案
1. 改造Controller,让Util可注入
首先确保Controller通过依赖注入获取Util实例,这样Mockito才能替换它:
@Controller public class YourController { // 推荐构造注入(Spring 4.3+支持无@Autowired的构造注入) private final Util util; public YourController(Util util) { this.util = util; } // ... 你的wellboreForm方法和其他代码 }
2. 修正测试类的Mock注入逻辑
使用@InjectMocks让Mockito把Mock的utilMock注入到Controller中,同时正确初始化MockMvc:
// JUnit 5用@ExtendWith,JUnit 4用@RunWith(MockitoJUnitRunner.class) @ExtendWith(MockitoExtension.class) public class YourControllerTest { @Mock private Util utilMock; @InjectMocks private YourController controller; // 替换成你的Controller类名 private MockMvc mockMvc; @BeforeEach // JUnit 5用这个,JUnit 4用@Before void setUp() { // 用standaloneSetup初始化MockMvc,关联你的Controller实例 mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); } // ... 你的测试方法 }
3. 正确设置Session属性
在MockMvc请求中设置Controller需要的userName和userRoles,而不是Mock独立的HttpServletRequest:
@Test @DisplayName("GET /wellbore - Select Wellbore") void testMockMvc() throws Exception { // 准备测试数据 String userName = "abcd"; Set<String> adNames = new HashSet<>(Arrays.asList("ROLE1", "ROLE2", userName)); Set<String> roleSet = new HashSet<>(Arrays.asList("ROLE1", "ROLE2")); // 配置Mock方法:如果参数匹配有问题,可以用宽松匹配(anyString(), anySet()) when(utilMock.getOperatorsAdNameWrapper(eq(userName), eq(adNames))).thenReturn(roleSet); // 或者宽松匹配:when(utilMock.getOperatorsAdNameWrapper(anyString(), anySet())).thenReturn(roleSet); // 执行请求,设置正确的Session属性 mockMvc.perform(get("/wellbore") .param("mode", "selectWellbore") .sessionAttr("userName", userName) .sessionAttr("userRoles", adNames) ) .andExpect(status().isOk()) .andExpect(view().name("ui/selectWellbore")) .andExpect(model().attribute("wellboreDataList", hasSize(2))); }
4. 处理参数匹配问题
如果你的测试中adNames和Controller从Session获取的operatorsSet不是同一个对象,Mockito的精确匹配会失效。这时候可以用参数匹配器:
eq():匹配相等的参数值anyString():匹配任意字符串anySet():匹配任意Set类型参数
根据你的测试场景选择合适的匹配方式即可。
验证
完成以上修改后,MockMvc执行请求时会使用你配置的utilMock实例,并且Session中的参数会正确传递到工具方法中,你的Mock配置就能正常生效了。
内容的提问来源于stack exchange,提问作者Noa
相关产品推荐
相关产品推荐

