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

nbnkbykc  于 2022-10-22  发布在  Java
关注(0)|答案(1)|浏览(156)

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

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。此外,在调试时,我可以看到传递给服务的参数与预期一样,但在这种情况下,由于某种原因,Assert将失败。当我将Assert更改为:

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

测试将通过,这意味着问题确实在于迭代器参数。这是测试失败时我收到的错误:
我的服务。myMethod(…,<自定义参数匹配器>);通缉1次:->在…(StorageClientTest.java:91)但通缉0次。
org.mockito.exceptions.verification。TooFewActualInvocations:myService。myMethod(…,<自定义参数匹配器>);通缉1次:->在…(StorageClientTest.java:91)但通缉0次。
我做错什么了吗?为什么测试失败?

wdebmtf2

wdebmtf21#

我从未使用过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()时,测试通过的原因是,您只验证存根是否被调用,但它肯定不等于您期望的值。
通过使用TestHelpers方法(它只返回true或false),您可以通过assertj更好地了解调用它的元素,以及它与预期元素的不同之处。

相关问题