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

Mockito测试含RestTemplate.delete与异常处理的服务方法问题

解决Mockito测试无返回值方法异常转换的问题

你遇到的核心问题是对@InjectMocks注入的对象使用doThrow的方式不对——因为@InjectMocks默认会创建被测试类的真实实例(而非Mock对象),所以不能用Mock的语法去模拟它的方法抛出异常。我们的目标是让真实的服务方法执行,通过Mock依赖的restTemplate抛出异常,来验证服务是否正确转换为自定义异常。

正确的测试实现步骤

  1. 正确配置Mock和被测试对象:用@Mock注解创建RestTemplate的Mock实例,用@InjectMocks创建真实的GreetingsService实例(确保依赖注入正确)。
  2. 指定Mock对象的异常行为:对restTemplate.delete方法使用doThrow,模拟它抛出404的HttpClientErrorException。
  3. 验证服务方法的异常输出:调用服务的真实方法,用JUnit的断言捕获并验证是否抛出了预期的GreetingNotFoundException。

完整测试代码示例

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.doThrow;

@ExtendWith(MockitoExtension.class)
public class GreetingsServiceTest {

    @Mock
    private RestTemplate restTemplate;

    @InjectMocks
    private GreetingsService greetingsService; // 真实实例,依赖被注入Mock的restTemplate

    @Test
    public void deleteGreetingById_WhenGreetingNotFound_ThrowsCustomException() {
        Integer testId = 1;
        String expectedUrl = UriComponentsBuilder.fromHttpUrl("http://localhost:9123/greetings/" + testId)
                .toUriString();

        // 配置Mock:当restTemplate调用delete指定URL时,抛出404异常
        doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND))
                .when(restTemplate)
                .delete(expectedUrl);

        // 调用真实服务方法,验证是否抛出预期异常
        GreetingNotFoundException exception = assertThrows(GreetingNotFoundException.class,
                () -> greetingsService.deleteGreetingById(testId));

        // 额外验证异常消息是否符合预期
        assertEquals("Greeting with id: " + testId + " not found.", exception.getMessage());
    }
}

关键知识点解释

  • @InjectMocks的作用:它的职责是创建被测试类的真实实例,并将所有标注了@Mock或@Spy的依赖注入到这个实例中。它本身不是Mock对象,所以不能用doThrow来模拟它的方法行为,我们要测试的就是它的真实逻辑。
  • 无返回值方法的Mock方式:对于restTemplate.delete这种无返回值的方法,使用doThrow(...).when(mock).method(...)是正确的语法,符合Mockito的规范。如果不想严格匹配URL参数,也可以用anyString()这样的匹配器,比如:
    doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND))
            .when(restTemplate)
            .delete(anyString());
    
  • 异常验证的正确姿势:JUnit 5的assertThrows是推荐的方式,它不仅能验证异常类型,还能捕获异常实例,方便我们进一步验证异常消息、原因等细节,比JUnit 4的expected属性更灵活。

内容的提问来源于stack exchange,提问作者FishingIsLife

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 22:37:41