Mockito获得NPE时,使用argThat自定义ArgumentMatcher



我尝试使用自定义ArgumentMatcher,但我总是得到NPE,对我来说现在很难找到原因,即使我在文档中找到了相关的NPE。

@Test
void debug() {
class ListGetMatcher implements ArgumentMatcher<Integer> {
public boolean matches(Integer index) {
return index == 2;
}
}
List mock = mock(List.class);
when(mock.get(argThat(new ListGetMatcher()))).thenReturn(200);
assertEquals(200, mock.get(2));
//when(mock.addAll(argThat(new ListOfTwoElements()))).thenReturn(true);
//mock.addAll(Arrays.asList("one", "two"));
//verify(mock).addAll(argThat(new ListOfTwoElements()));
}

我把它改成了intThat,效果很好。

doReturn(200).when(mock).get(intThat(new ListGetMatcher()));

但以下情况仍会引起NPE

when(substationQueryExecutor.getSubById(argThat(argument -> argument.getId() == 1L))).thenReturn(new Substation());

我期望当参数的id为999时返回一个变电站。我该怎么办?


下面是更详细的代码,我是getSubById()的存根,它具有SubstationQueryCmd参数类型,并且我期望根据SubstationQueryCmd的id的值返回不同的值。当我调试代码时,我发现对matches()的回调总是传递给我一个空,而不是SubstationQueryCmd对象。我不明白是什么问题,也许你知道。

@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
class SubPowerSaveExecutorTest {
@InjectMocks
private SubPowerSaveExecutor executor;
@Mock
private SubstationQueryExecutor queryExecutor;
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
}
static class SubstationQueryArgMatcher implements ArgumentMatcher<SubstationQueryCmd> {
private final Long id;
public SubstationQueryArgMatcher(Long id) {
this.id = id;
}
@Override
public boolean matches(SubstationQueryCmd argument) {
// I debugged the code and found that the argument is null every time, I think this is the problem
return argument.getId().longValue() == id;
}
}
@Test
void test() {
when(queryExecutor.getSubById(argThat(new SubstationQueryArgMatcher(1L)))).thenReturn(new Substation());
when(queryExecutor.getSubById(argThat(new SubstationQueryArgMatcher(999L)))).thenReturn(null);
List<SubPowerDto> datas = new ArrayList<>();
datas.add(new SubPowerDto().setMeterNum("meter01").setProviderName("diehl").setDate("2022-04-24 10:10:00").setSubstationId(1L).setPower(1D));
datas.add(new SubPowerDto().setMeterNum("meter01").setProviderName("diehl").setDate("2022-04-24 10:20:00").setSubstationId(1L).setPower(1D));
datas.add(new SubPowerDto().setMeterNum("meter01").setProviderName("diehl").setDate("2022-04-24 10:30:00").setSubstationId(1L).setPower(1D));
datas.add(new SubPowerDto().setMeterNum("meter01").setProviderName("diehl").setDate("2022-04-24 10:30:00").setSubstationId(999L).setPower(1D));
// getSubById() will be called in filterData
List<SubPower> subPowers = executor.filterData(datas);
assertEquals(1, subPowers.size());
}
}

因为您使用的是argThat。作为来源:

public static <T> T argThat(Matcher<T> matcher) {
return reportMatcher(matcher).<T>returnNull();
}

要解决这个问题,只需更改为使用intThat

public static int intThat(Matcher<Integer> matcher) {
return reportMatcher(matcher).returnZero();
}

你的第二个问题:你可能误解了ArgumentMatcher的用法。

你的方法queryExecutor.getSubById有参数作为Long,但你使用:

ArgumentMatcher<SubstationQueryCmd>

这是不正确的。改为:

ArgumentMatcher<Long>

那么你可以用longThat代替argThat


如果substationQueryExecutor.getSubById有参数作为SubstationQueryCmd

然后你只需要检查null在你的自定义匹配器:

@Override
public boolean matches(SubstationQueryCmd argument) {
if (argument == null) {
return false;
}
return argument.getId().longValue() == id;
}

因为:那是一个对象,而且有可能是null。这发生在你想要存根的直线上。因此,如果您使用以下语法,则需要在自定义参数匹配器中检查null:

when(...).thenReturn(...)

为了避免更改自定义参数匹配器,您可以改为使用以下语法:

doReturn(...).when(yourMock).doSomething()
所以你的代码应该是:
doReturn(new Substation()).when(queryExecutor).getSubById(argThat(new SubstationQueryArgMatcher(1L)));
doReturn(null).when(queryExecutor).getSubById(argThat(new SubstationQueryArgMatcher(999L)));

编码快乐!

最新更新