Mockito arg用于匹配迭代器arg时不起作用



我正在尝试验证是否使用参数调用了模拟服务,其中最后一个参数是迭代器。这是测试中的断言:

verify(myService).myMethod(
...,
argThat(dataIterator -> iteratorEquals(dataIterator, dataToSave.iterator())));

我有一个TestHelper方法:

public class TestHelpers {
public static <T> boolean iteratorEquals(Iterator<T> x, Iterator<T> y) {
while (x.hasNext() && y.hasNext()) {
if (x.next() != y.next()) {
return false;
}
}
return x.hasNext() == y.hasNext();
}
}

我正在调试静态方法,从中返回的值似乎确实是true。此外,在调试时,我可以看到传递给服务的参数是预期的,但在这种情况下,由于某种原因,断言将失败。当我将断言更改为:

verify(myService).myMethod(
...,
any());

测试将通过,这意味着问题确实出在迭代器参数上。这是我在测试失败时收到的错误:

myService.myMethod(。。。,<自定义参数匹配器>(;通缉1次:->在(StorageClientTest.java:91(但是是0次。

org.mockito.exceptions.verification.ToFewActualInvocations:myService.myMethod(。。。,<自定义参数匹配器>(;通缉1次:->在(StorageClientTest.java:91(但是是0次。

我是不是做错了什么?为什么测试失败了?

我从未使用过Mockito.argThat,部分原因是我从未需要定义自定义参数匹配器。

当我正确理解您的用例时,那么您希望测试某个使用类型为MyService的存根的类。然后,您需要验证存根是用Iterable调用的,并且Iterable中的元素与另一个可迭代元素的顺序相同。

在您的情况下,我将使用ArgumentCaptor<>,然后根据一些类似的预期值验证捕获的参数。

@ExtendWith(MockitoExtension.class)
class MyControllerTest {
@Captor
ArgumentCaptor<List<Data>> dataArgumentCaptor;
@Test
void save() {
// Arrange
final var service = mock(MyService.class);
final var controller = new MyController(service);
final var myExpectedData = List.of(new Data("Foo"), new Data("Bar"));

// Act
controller.save("Foo", "Bar");
// Assert
verify(service).save(dataArgumentCaptor.capture());
assertThat(dataArgumentCaptor.getValue()).containsExactlyElementsOf(myExpectedData);
// when you dont use assertj you could assert them also with plain junit like that:
assertArrayEquals(dataToSave.toArray(), myExpectedData.toArray());
}
class MyService {
public void save(List<Data> data) {
}
}
record Data(String n) {
}
class MyController {
private final MyService service;
public MyController(MyService service) {
this.service = service;
}
public void save(String... data) {
final var list = Arrays.stream(data).map(Data::new).toList();
service.save(list);
}
}
}

当你使用any()时,你的测试之所以通过,是因为你只验证你的存根是用什么调用的,但它肯定不等于你期望的。

通过使用只返回true或false的TestHelpers方法,您可以通过assertj更好地了解调用它的元素以及它与预期元素的不同之处。

这个问题的解决方案是更改通用帮助方法的签名以返回ArgumentMatcher,并将lambda表达式移动到其中:

public <T> ArgumentMatcher<Iterator<T>> equalsIterator(Iterator<T> compareTo) {
return x -> {
while (x.hasNext() && compareTo.hasNext()) {
if (x.next() != compareTo.next()) {
return false;
}
}
return x.hasNext() == compareTo.hasNext();
};
}

我这样称呼它:

verify(myService).myMethod(
...,
argThat(equalsIterator(dataToSave.iterator())));

最新更新