Spring Boot中Mockito测试抛出异常失败问题排查及测试建议
Spring Boot服务类异常测试问题:无法触发IllegalArgumentException
问题背景
作为Spring Boot新手,尝试测试服务类在保存失败时是否会正确处理IllegalArgumentException,但始终无法触发异常场景,测试方法2返回状态码201而非预期的500。
服务类代码片段
public ResponseEntity<ModelApiResponse> createResponse(Response response) throws JsonProcessingException, IllegalArgumentException{ try{ chatResponseRepository.save(response); return new ResponseEntity<>(HttpStatus.valueOf(GlobalVariables.RESPONSE_CODE_201)); } catch (IllegalArgumentException ex){ return new ResponseEntity<>(HttpStatus.valueOf(GlobalVariables.RESPONSE_CODE_500)); } }
两种测试方法及问题
测试方法1
@Test public void testThatResponseCreationThrowsErrorWhenSavingFails() throws Exception{ Response responseMock = mock(Response.class); when(chatResponseRepositoryMock.save(new Response())).thenThrow(IllegalArgumentException.class); assertThrows(IllegalArgumentException.class, () -> abbyResponseRepositoryMock.save(abbyResponseMock)); }
问题:该测试完全偏离目标——直接调用Mock仓库的save方法,根本没触发服务类的createResponse逻辑,服务类的try-catch处理完全没被执行,也验证不了服务类的异常处理行为。
测试方法2
@Test public void testThatResponseCreationThrowsErrorWhenSavingFails() throws Exception { Mockito.when(chatResponseRepositoryMock.save(new Response())).thenThrow(Mockito.mock(IllegalArgumentException.class)); Response response = new Response(); response.setMessage("This is a unit test response message"); response.setNxtMessageId("98765"); mockMvc.perform(post("/api/v1/responses/create-response") .header(HttpHeaders.AUTHORIZATION, "Basic " + "credentials") .header(GlobalVariables.X_CORRELATION_ID, "123456") .header(GlobalVariables.X_SOURCE_APP, "source_app") .header(GlobalVariables.X_USER, "user") .header(GlobalVariables.X_USER_ROLE, "user") .content(this.json(response)) .contentType(contentType)) .andExpect(jsonPath("$.header.responseCode", is(500))) .andExpect(jsonPath("$.header.responseMessage", is("Error saving chat response"))) .andReturn(); }
测试结果:
Expected: is <500> but: was <201>
问题:Mockito的匹配条件用了save(new Response()),这要求传入的对象和new Response()是同一个实例,但实际测试中传入的是自定义了message和nxtMessageId的Response对象,实例不匹配,导致Mock的异常抛出逻辑未触发,仓库执行默认Mock行为,服务类走正常流程返回201。
解决方案
修正测试方法1(服务类单元测试)
调整Mock匹配规则为任意Response实例,同时断言服务类返回的状态码(注意服务类已捕获异常,不会向外抛出):
@Test public void testThatResponseCreationReturns500WhenSavingFails() throws Exception{ // 匹配任意Response实例,触发异常 when(chatResponseRepositoryMock.save(any(Response.class))).thenThrow(IllegalArgumentException.class); // 调用服务类核心方法 ResponseEntity<ModelApiResponse> result = yourService.createResponse(new Response()); // 断言返回状态码为500 assertEquals(HttpStatus.valueOf(GlobalVariables.RESPONSE_CODE_500), result.getStatusCode()); }
修正测试方法2(控制器集成测试)
同样修改Mock的匹配规则为任意Response实例,同时确保服务类返回的ResponseEntity包含正确的ModelApiResponse对象(否则jsonPath断言会失败):
@Test public void testThatCreateResponseEndpointReturns500WhenSavingFails() throws Exception { // 匹配任意Response实例,触发异常 Mockito.when(chatResponseRepositoryMock.save(any(Response.class))).thenThrow(IllegalArgumentException.class); Response response = new Response(); response.setMessage("This is a unit test response message"); response.setNxtMessageId("98765"); mockMvc.perform(post("/api/v1/responses/create-response") .header(HttpHeaders.AUTHORIZATION, "Basic " + "credentials") .header(GlobalVariables.X_CORRELATION_ID, "123456") .header(GlobalVariables.X_SOURCE_APP, "source_app") .header(GlobalVariables.X_USER, "user") .header(GlobalVariables.X_USER_ROLE, "user") .content(this.json(response)) .contentType(contentType)) .andExpect(status().is(500)) .andExpect(jsonPath("$.header.responseCode", is(500))) .andExpect(jsonPath("$.header.responseMessage", is("Error saving chat response"))) .andReturn(); }
额外注意:服务类的catch块需要正确构造包含header信息的ModelApiResponse对象,否则响应体为空,jsonPath断言会失败,例如:
catch (IllegalArgumentException ex){ ModelApiResponse errorResponse = new ModelApiResponse(); errorResponse.setHeader(new ResponseHeader(500, "Error saving chat response")); return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(GlobalVariables.RESPONSE_CODE_500)); }
测试方法选型建议
- 测试方法1(单元测试):更适合单独验证服务类的核心逻辑,速度快,能精准覆盖服务层的异常处理、状态码返回等逻辑,是日常开发中优先编写的测试用例。
- 测试方法2(集成测试):用于验证从HTTP请求到控制器、服务、仓库的完整流程,确保端到端的请求响应符合业务预期,适合作为补充测试,覆盖接口级别的场景。
内容的提问来源于stack exchange,提问作者Kibet Kyle
相关产品推荐
相关产品推荐

