简体   繁体   English

mockito验证与ArgumentCaptor的交互

[英]mockito verify interactions with ArgumentCaptor

To check the number of interactions with a mock where the parameter in the method call is of a certain type, one can do 要检查与模拟的交互次数,其中方法调用中的参数是某种类型,可以做

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

This will pass thanks to the call to isA since someMethod was called twice but only once with argument FirstClass 这将通过调用isA因为someMethod被调用两次,但只有一次使用参数FirstClass

However, this pattern seems to not be possible when using an ArgumentCaptor, even if the Captor was created for the particular argument FirstClass 但是,使用ArgumentCaptor时,这种模式似乎是不可能的,即使Captor是为特定参数FirstClass创建的

this doesn't work 这不起作用

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

it says the mock was called more than once. 它说模拟不止一次被召唤。

Is there any way to accomplish this verification while capturing the argument for further checking? 在获取进一步检查的参数时,有没有办法完成此验证?

I recommend using Mockito's Hamcrest integration to write a good, clean matcher for it. 我建议使用Mockito的Hamcrest集成为它编写一个好的,干净的匹配器。 That allows you to combine the verification with detailed checking of the passed argument: 这允许您将验证与对传递参数的详细检查结合起来:

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);
        }
    };
}

Matchers generally lead to more readable tests and more useful test failure messages. 匹配器通常会导致更可读的测试和更有用的测试失败消息。 They also tend to be very reusable, and you'll find yourself building up a library of them tailored for testing your project. 它们也往往是非常可重用的,你会发现自己建立了一个为测试你的项目量身定制的库。 Finally, you can also use them for normal test assertions using JUnit's Assert.assertThat() , so you get double use out of them. 最后,您还可以使用JUnit的Assert.assertThat()将它们用于正常的测试断言,因此您可以使用它们进行双重使用。

Quoting the docs: 引用文档:

Note that an ArgumentCaptor don't do any type checks , it is only there to avoid casting in your code. 请注意, ArgumentCaptor 不进行任何类型检查 ,只是为了避免在代码中进行转换。 This might however change (type checks could be added) in a future major release. 但是,这可能会在未来的主要版本中发生变化(可以添加类型检查)。

I wouldn't use an ArgumentCaptor for this. 我不会为此使用ArgumentCaptor This class captures (literally) everything, despite what class was provided as it's .forClass argument. 这个类捕获(字面上)所有内容,尽管提供了类的.forClass参数。

To achieve what you want I suggest intercept the argument using Mockito's Answer interface: 为了达到你想要的效果,我建议使用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));
}

You can use the the captor for the sake of capturing, then verify the number of invocations with each argument type separately. 您可以使用捕获器来捕获,然后分别验证每个参数类型的调用次数。

    // given
    ArgumentCaptor<AA> captor = ArgumentCaptor.forClass(AA.class);
    CC cc = new CC();
    // when
    cut.someMethod(new AA());
    cut.someMethod(new BB());
    cut.someMethod(new BB());
    cut.someMethod(cc);
    // then
    Mockito.verify(collaborator, atLeastOnce()).someMethod(captor.capture());
    Mockito.verify(collaborator, times(1)).someMethod(isA(AA.class));
    Mockito.verify(collaborator, times(2)).someMethod(isA(BB.class));
    Mockito.verify(collaborator, times(1)).someMethod(isA(CC.class));
    assertEquals(cc, captor.getValue());

Apparently the generic type of the captor reference doesn't affect anything at runtime. 显然,captor引用的泛型类型不会影响运行时的任何内容。

I also encountered this problem today. 我今天也遇到了这个问题。 I thought I could simply do something like 我以为我可以简单地做一些事情

verify(mock).someMethod(and(isA(FirstClass.class), captor.capture()));

but I couldn't get it to work. 但我无法让它发挥作用。 I ended up with this solution: 我最终得到了这个解决方案:

@Test
public void Test() throws Exception {
    final ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);

    mock.someMethod(new FirstClass());
    mock.someMethod(new OtherClass());

    verify(eventBus, atLeastOnce()).post(captor.capture());
    final List<FirstClass> capturedValues = typeCheckedValues(captor.getAllValues(), FirstClass.class);
    assertThat(capturedValues.size(), is(1));
    final FirstClass capturedValue = capturedValues.get(0);
    // Do assertions on capturedValue
}

private static <T> List<T> typeCheckedValues(List<T> values, Class<T> clazz) {
    final List<T> typeCheckedValues = new ArrayList<>();
    for (final T value : values) {
        if (clazz.isInstance(value)) {
            typeCheckedValues.add(value);
        }
    }
    return typeCheckedValues;
}

Note: if only one class needs to be captured in this way typeCheckedValues can be simplified into: 注意:如果只需要以这种方式捕获一个类,则typeCheckedValues可以简化为:

private static List<FirstClass> typeCheckedValues(List<FirstClass> values) {
    final List<FirstClass> typeCheckedValues = new ArrayList<>();
    for (final Object value : values) {
        if (value instanceof FirstClass) {
            typeCheckedValues.add((FirstClass) value);
        }
    }
    return typeCheckedValues;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM