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

JUnit测试遇AssertionError:预期<1>实际<30>,求错误排查方案

JUnit & Mockito测试断言错误排查:expected:<1> but was:<30>

Hey there! Let's break down why you're hitting this assertion error and fix it step by step.

问题回顾

作为JUnit和Mockito新手,你在测试GitHub用户列表获取代码时遇到了这个异常:

java.lang.AssertionError: expected:<1> but was:<30>

对应的堆栈信息:

at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at org.junit.Assert.assertEquals(Assert.java:631)

原业务代码

public List<GithubModel> getGitHubUsersList(){
    HttpHeaders httpHeaders = new HttpHeaders();
    HttpEntity<GithubModel>httpEntity = new HttpEntity<GithubModel>(httpHeaders);
    ResponseEntity<List<GithubModel>>usersList = restTemplate.exchange("https://api.github.com/repos/git/git/contributors", HttpMethod.GET,httpEntity, new ParameterizedTypeReference<List<GithubModel>>() { });
    return usersList.getBody();
}

测试用例代码

@Spy
@InjectMocks
MicroServicesTest1 microServicesTest1;
@Mock
RestTemplate mockRestTemplate;
@Before
public void create(){
    MockitoAnnotations.initMocks(this);
}
@Test
public void getGitHubUsersListTest(){
    ResponseEntity<List<GithubModel>> response = Mockito.mock(ResponseEntity.class);
    Mockito.when(mockRestTemplate.exchange(Mockito.anyString(), Mockito.any(HttpMethod.class), Mockito.any(HttpEntity.class), Mockito.eq(new ParameterizedTypeReference<List<GithubModel>>(){}))).thenReturn(response);
    Mockito.when(response.getBody()).thenReturn(getUsersList());
    List<GithubModel> mocklist = microServicesTest1.getGitHubUsersList();
    assertEquals(1, mocklist.size());
}
/**
 * getUsersList
 * @return
 */
public List<GithubModel>getUsersList(){
    GithubModel githubModel = new GithubModel();
    githubModel.setId(1);
    githubModel.setLogin("hello1");
    githubModel.setNode_id("hello2");
    List<GithubModel>list = new ArrayList<>();
    list.add(githubModel);
    return list;
}

问题根源

核心问题出在**Mockito.eq(new ParameterizedTypeReference<List<GithubModel>>(){})**这个匹配逻辑上:

  • 你创建的是匿名内部类实例,Java会为每个匿名内部类生成独立的类对象,哪怕代码完全一样,不同实例的equals()判断是基于对象引用的,所以测试里的实例和业务代码里的实例不匹配。
  • 再加上你给MicroServicesTest1加了@Spy注解,当mock匹配失败时,Spy会默认调用真实的业务方法——也就是说你的测试实际上请求了GitHub的真实API,拿到了git/git仓库的真实贡献者列表(刚好是30条左右),所以断言assertEquals(1, mocklist.size())才会失败。

解决方法

方法一:用any()匹配ParameterizedTypeReference

最简单的修复是把精确匹配换成模糊匹配,确保mock能被触发:

Mockito.when(mockRestTemplate.exchange(
    Mockito.anyString(), 
    Mockito.any(HttpMethod.class), 
    Mockito.any(HttpEntity.class), 
    Mockito.any(ParameterizedTypeReference.class)
)).thenReturn(response);

方法二:提取ParameterizedTypeReference为常量

如果需要精确匹配,可以把业务代码中的ParameterizedTypeReference提取成静态常量,让业务和测试代码共用同一个实例:
首先修改业务代码:

private static final ParameterizedTypeReference<List<GithubModel>> GITHUB_CONTRIBUTORS_TYPE = 
    new ParameterizedTypeReference<List<GithubModel>>() {};

public List<GithubModel> getGitHubUsersList(){
    HttpHeaders httpHeaders = new HttpHeaders();
    HttpEntity<GithubModel>httpEntity = new HttpEntity<GithubModel>(httpHeaders);
    ResponseEntity<List<GithubModel>>usersList = restTemplate.exchange(
        "https://api.github.com/repos/git/git/contributors", 
        HttpMethod.GET,
        httpEntity, 
        GITHUB_CONTRIBUTORS_TYPE
    );
    return usersList.getBody();
}

然后修改测试用例的匹配逻辑:

Mockito.when(mockRestTemplate.exchange(
    Mockito.anyString(), 
    Mockito.any(HttpMethod.class), 
    Mockito.any(HttpEntity.class), 
    Mockito.eq(MicroServicesTest1.GITHUB_CONTRIBUTORS_TYPE)
)).thenReturn(response);

额外优化建议

  1. 去掉@Spy注解:除非你需要部分mock业务类,否则只用@InjectMocks创建实例即可——这样mock匹配失败时会返回默认值(比如空集合),更容易定位问题。
  2. 替换过时方法:用MockitoAnnotations.openMocks(this)替代MockitoAnnotations.initMocks(this),后者已经被标记为过时。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:25:08