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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:05:57