循环使用Mockito argThat出现NullPointerException问题求助
Mockito循环Stub时空指针异常的原因与解决办法
问题背景
循环使用Mockito的argThat进行方法Stub时,当迭代次数≥2会抛出空指针异常:
java.lang.NullPointerException: Cannot invoke "getValue()" because "arg" is null
测试代码片段:
@Test public void testMy() { int numberOfItems = 2; List<MyData> myDataList = getTestData(numberOfItems); for (MyData md : myDataList) { // 此处触发NPE when(mockedService.someMethod( argThat(arg -> Objects.equals(arg.getValue(), md.getMyValue())))) .thenReturn(getExpectedResult(md.getMyValue())); } List<MyData> resData = testService.methodUnderTest(myDataList); // 断言逻辑 }
被测方法逻辑:
public List<MyData> methodUnderTest(List<MyData> myDataList) { myDataList.forEach(myData -> { // 业务处理逻辑 myData.setResult(mockedService.someMethod(new MyRequest(myData.getMyValue()))); } }); return myDataList; }
原使用Hamcrest匹配器的写法可正常运行:
when(mockedService.someMethod( argThat(hasProperty("value", equalTo(md.getMyValue()))))) .thenReturn(getExpectedResult(md.getMyValue()));
原因分析
- Mockito的Stub执行机制:调用
when(mock.method(argThat(...)))时,Mockito会实际执行一次被mock的方法(mock对象默认返回null),这个过程中argThat的lambda匹配器会被触发执行。 - 自定义lambda未做空指针防护:直接调用
arg.getValue()时,若Mockito内部传入null作为校验参数(比如第二次Stub时,方法调用不符合第一次的匹配规则,触发null参数校验),就会抛出NPE;而Hamcrest的hasProperty匹配器内部已处理空指针场景,不会报错。 - 循环Stub的叠加影响:第一次Stub后,mock方法的行为已被记录,第二次Stub时的方法调用会触发Mockito对已有匹配器的校验,此时未防护的lambda就会触发空指针异常。
解决方案
方案1:在lambda中添加空指针校验
修改argThat的lambda表达式,先判断参数非空再调用方法:
when(mockedService.someMethod( argThat(arg -> arg != null && Objects.equals(arg.getValue(), md.getMyValue())))) .thenReturn(getExpectedResult(md.getMyValue()));
方案2:改用doReturn语法跳过方法调用
doReturn不会触发mock方法的实际执行,从根源避免匹配器被提前执行:
for (MyData md : myDataList) { doReturn(getExpectedResult(md.getMyValue())) .when(mockedService) .someMethod(argThat(arg -> Objects.equals(arg.getValue(), md.getMyValue()))); }
方案3:使用eq匹配器(需重写equals)
如果MyRequest类正确重写了equals和hashCode,可以直接用eq匹配器简化代码:
when(mockedService.someMethod(eq(new MyRequest(md.getMyValue())))) .thenReturn(getExpectedResult(md.getMyValue()));
内容的提问来源于stack exchange,提问作者RAM237
相关产品推荐
相关产品推荐

