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

编写JUnit测试遇匹配器错误,求教Mock内部创建的RestTemplate

JUnit测试问题解决:"0 matchers expected, 1 recorded"错误及Mock RestTemplate方案

一、错误原因分析

"0 matchers expected, 1 recorded"错误是因为在真实方法调用中使用了Mockito匹配器(如any())。比如你的getTest方法里idmService.get(endPoint, any(), true),any()这类匹配器只能用于Mockito的when()/verify()方法,不能作为真实方法的参数传递。

二、代码重构:让RestTemplate可Mock

你的exchange方法里直接new RestTemplate(...),这种局部创建的对象无法被Mock,必须通过依赖注入将RestTemplate注入到类中,才能进行测试。重构后的TestServiceImpl:

@Service
public class TestServiceImpl implements TestService {

    // 注入RestTemplate,替代方法内new的方式
    private final RestTemplate restTemplate;

    // 构造方法注入(推荐方式,符合依赖注入原则)
    public TestServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        // 配置HttpComponentsClientHttpRequestFactory
        this.restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
    }

    @Override
    public Object get(String endpoint, ParameterizedTypeReference reference, boolean useToken) {
        // 补全原代码缺失的userId参数
        ResponseEntity response = exchange(HttpMethod.GET, endpoint, null, reference, useToken, "defaultUserId");
        return response.getBody();
    }

    @Override
    public ResponseEntity<Object> exchange(HttpMethod httpMethod, String endpoint, @Nullable Object body, 
                                           ParameterizedTypeReference<Object> reference, boolean useToken, String userId) {
        try {
            HttpHeaders headers = new HttpHeaders();
            // 这里可以添加headers的业务逻辑(比如token处理)
            ResponseEntity<Object> response = restTemplate.exchange(endpoint, httpMethod, 
                                                                   new HttpEntity<>(body, headers), reference);
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            // 返回错误响应,避免返回null
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

三、正确编写测试用例

1. get方法测试(Mock本类的exchange方法)

get方法依赖本类的exchange方法,用Mockito Spy可以保留真实类的逻辑,同时Mockexchange方法的返回,验证get方法的正确性:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.core.ParameterizedTypeReference;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;
import static org.mockito.ArgumentMatchers.*;

@ExtendWith(MockitoExtension.class)
public class TestServiceImplTest {

    @Spy
    @InjectMocks
    private TestServiceImpl testService; // Spy对象:保留真实逻辑,可Mock指定方法

    @Mock
    private RestTemplate restTemplate; // 注入依赖,满足构造方法要求

    @Test
    public void getTest() {
        // 准备测试数据
        String endpoint = "/api/test";
        ParameterizedTypeReference<Object> typeRef = new ParameterizedTypeReference<>() {};
        ResponseEntity<Object> mockResponse = ResponseEntity.ok("mockGetBody");

        // Mock exchange方法的返回
        doReturn(mockResponse)
                .when(testService)
                .exchange(eq(HttpMethod.GET), eq(endpoint), isNull(), eq(typeRef), eq(true), eq("defaultUserId"));

        // 调用get方法
        Object result = testService.get(endpoint, typeRef, true);

        // 验证结果
        assertEquals("mockGetBody", result);
    }
}

2. exchange方法测试(Mock RestTemplate的exchange方法)

测试exchange方法时,Mock注入的RestTemplate,验证它是否被正确调用,并返回预期结果:

@Test
public void exchangeTest() {
    // 准备测试数据
    String endpoint = "/api/exchange";
    HttpMethod httpMethod = HttpMethod.POST;
    Object body = new Object();
    ParameterizedTypeReference<Object> typeRef = new ParameterizedTypeReference<>() {};
    boolean useToken = true;
    String userId = "testUserId";
    ResponseEntity<Object> mockResponse = ResponseEntity.ok("mockExchangeBody");

    // Mock RestTemplate的exchange方法
    doReturn(mockResponse)
            .when(restTemplate)
            .exchange(eq(endpoint), eq(httpMethod), any(HttpEntity.class), eq(typeRef));

    // 调用exchange方法
    ResponseEntity<Object> result = testService.exchange(httpMethod, endpoint, body, typeRef, useToken, userId);

    // 验证结果
    assertEquals("mockExchangeBody", result.getBody());
    // 可选:验证RestTemplate的exchange方法是否被调用一次
    // verify(restTemplate, times(1)).exchange(eq(endpoint), eq(httpMethod), any(HttpEntity.class), eq(typeRef));
}

四、关键注意事项

  • 禁止在真实方法调用中使用Mockito匹配器(如any()、eq()),匹配器仅用于Mockito的when()/verify()方法。
  • 避免在方法内直接创建依赖对象(如new RestTemplate()),通过依赖注入管理依赖,便于测试和维护。
  • 使用@Spy时,确保被Mock的方法是非final的,否则Mockito无法进行Mock操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:07:47