mockito验证与ArgumentCaptor的交互

Hil*_*kus 25 java unit-testing mocking mockito

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

mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));
Run Code Online (Sandbox Code Playgroud)

这将通过感谢调用isA自从someMethod被调用两次,但只有一次与参数FirstClass

但是,使用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());
Run Code Online (Sandbox Code Playgroud)

它说模拟不止一次被召唤.

在获取进一步检查的参数时,有没有办法完成此验证?

Rya*_*art 27

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

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);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

  • @fge:严格地说,不是没有,但是如果我们只回答了问题,那将是一个更无聊的地方,人们会学到更少的东西.我认为这是对所提出的整体问题的正确解决方案,即使实际问题,正如措辞所说,并不是要求这样做. (11认同)
  • 这并没有真正回答OP的问题,是吗? (3认同)
  • 这是最优雅的解决方案,也是我实际使用的解决方案 (2认同)
  • 我强烈支持@ RyanStewart的断言.有时你需要解决OP*应该*真正做的事情,而不是回答他们的直接问题 (2认同)

Mar*_*des 6

引用文档:

请注意,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));
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,我不知道mockito有自己的可以使用的`isA`版本。但我仍然更喜欢使用“times(1)”或“only()”清楚地说明预期方法调用的数量,以提高可读性(IMO)。 (2认同)