mockito 验证与 ArgumentCaptor 的交互

2022-09-01 10:46:02

要检查与模拟的交互次数,其中方法调用中的参数是某种类型的,可以执行以下操作

mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));

这将通过由于调用,因为被调用了两次,但只有一次使用参数 FirstClassisAsomeMethod

但是,在使用 ArgumentCaptor 时,这种模式似乎是不可能的,即使 Captor 是为特定参数创建的。FirstClass

这不起作用

mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
verify(mock, times(1)).someMethod(captor.capture());

它说模拟被叫了不止一次。

有没有办法在捕获参数以进行进一步检查的同时完成此验证?


答案 1

我建议使用Mockito的Hamcrest集成来为它编写一个好的,干净的匹配器。这允许您将验证与传递的参数的详细检查相结合:

import static org.mockito.hamcrest.MockitoHamcrest.argThat;

verify(mock, times(1)).someMethod(argThat(personNamed("Bob")));

Matcher<Person> personNamed(final String name) {
    return new TypeSafeMatcher<Person>() {
        public boolean matchesSafely(Person item) {
            return name.equals(item.getName());
        }
        public void describeTo(Description description) {
            description.appendText("a Person named " + name);
        }
    };
}

匹配器通常会导致更具可读性的测试和更有用的测试失败消息。它们也往往是非常可重用的,你会发现自己正在构建一个为测试你的项目而量身定制的库。最后,您还可以使用它们进行使用 JUnit 的正常测试断言,因此您可以从中获得双重用途。Assert.assertThat()


答案 2

引用文档:

请注意,不要执行任何类型检查,它只是为了避免在代码中强制转换。但是,这可能会在未来的主要版本中发生变化(可以添加类型检查)。ArgumentCaptor

我不会为此使用。这个类(从字面上)捕获所有内容,尽管它的参数提供了什么类。ArgumentCaptor.forClass

为了实现你想要的,我建议使用Mockito的接口拦截参数:Answer

private FirstClass lastArgument;

@Test
public void captureFirstClass() throws Exception {
    doAnswer(captureLastArgument()).when(mock).someMethod(anInstanceOfFirstClass());
    mock.someMethod(new FirstClass());
    mock.someMethod(new OtherClass());

    verify(mock, times(1)).someMethod(anInstanceOfFirstClass());
    //write your desired matchers against lastArgument object
}

private Answer<FirstClass> captureLastArgument() {
    return new Answer<FirstClass>() {
        @Override
        public FirstClass answer(InvocationOnMock invocation) throws Throwable {
            TestClass.this.lastArgument = (FirstClass) invocation.getArguments()[0];
            return null;
        }
    };
}

private static Object anInstanceOfFirstClass(){
    return Mockito.argThat(isA(FirstClass.class));
}

推荐