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);
额外优化建议
- 去掉
@Spy注解:除非你需要部分mock业务类,否则只用@InjectMocks创建实例即可——这样mock匹配失败时会返回默认值(比如空集合),更容易定位问题。 - 替换过时方法:用
MockitoAnnotations.openMocks(this)替代MockitoAnnotations.initMocks(this),后者已经被标记为过时。
内容的提问来源于stack exchange,提问作者Krish
相关产品推荐
相关产品推荐

