Java Mockito:等待匹配参数的调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22944202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 19:29:53  来源:igfitidea点击:

Mockito: wait for an invocation that matches arguments

javatestingseleniumasynchronousmockito

提问by Bryan Hart

I'm writing a selenium test and verifying the server behavior with mockito. Specifically, when a button is clicked, I want to make sure the page controller calls a particular method on a dependency which I've mocked.

我正在编写一个 selenium 测试并使用 mockito 验证服务器行为。具体来说,当单击按钮时,我想确保页面控制器对我模拟的依赖项调用特定方法。

Because it is a selenium test, I need to wait for the mock to be invoked in another thread, so I'm using mockito timeout.

因为是selenium测试,所以需要等待mock在另一个线程中被调用,所以我用的是mockito timeout。

verify(myMock, timeout(5000).times(1)).myMethod("expectedArg");

The trouble that I'm having is that myMethod is called many times... rather than waiting for an invocation that matches the expected arguments, timeout only waits for the first invocation. If I use Thread.sleep(50000) rather than timeout(50000), it works as expected... but that's dirty so I'm hoping to avoid it.

我遇到的问题是 myMethod 被多次调用......而不是等待与预期参数匹配的调用,超时只等待第一次调用。如果我使用 Thread.sleep(50000) 而不是 timeout(50000),它会按预期工作……但这很脏,所以我希望避免它。

How do I wait for myMethod to be invoked with the expected input?

如何等待使用预期输入调用 myMethod ?

回答by fge

This is not a super clean solution but you can do this (XXis the supposed return type here):

这不是一个超级干净的解决方案,但您可以这样做(XX这里是假定的返回类型):

final CountDownLatch latch = new CountDownLatch(1);

doReturn(new Answer<XX>()
    {
        @Override
        public XX answer(InvocationOnMock invocation)
        {
            latch.countDown();
            return someInstanceOfXX;
        }
    }
).when(myMock).myMethod("expectedArg");

Then, to test if the method is called, do:

然后,要测试该方法是否被调用,请执行以下操作:

try {
    assertTrue(latch.await(5L, TimeUnit.SECONDS));
} catch (InterruptedException e) {
    // Urgh... Failed. Deal with it and:
    Thread.currentThread.interrupt();
}

回答by juhoautio

If you are able to set a fixed number of calls to expect, it can be done with an ArgumentCaptor:

如果您能够设置预期的固定呼叫次数,则可以使用以下命令来完成ArgumentCaptor

import static org.hamcrest.CoreMatchers.hasItem;

@Captor ArgumentCaptor<String> arg;

@Before
public void setUp() throws Exception {
    // init the @Captor
    initMocks(this);
}

@Test
public void testWithTimeoutCallOrderDoesntMatter() throws Exception {
    // there must be exactly 99 calls
    verify(myMock, timeout(5000).times(99)).myMethod(arg.capture());
    assertThat(arg.getAllValues(), hasItem("expectedArg"));
}

Another way is to specify all the expected values to verify, but those need to be provided in the exact order that they are invoked. The difference to the above solution is that this doesn't fail even if the mock is additionally called with some non-verified arguments. In other words, no need to know the number of total invocations. Code example:

另一种方法是指定要验证的所有预期值,但需要按照调用它们的确切顺序提供这些值。与上述解决方案的不同之处在于,即使使用一些未验证的参数额外调用模拟,这也不会失败。换句话说,不需要知道总调用次数。代码示例:

@Test
public void testWithTimeoutFollowingCallsDoNotMatter() throws Exception {
    // the order until expected arg is specific
    verify(callback, timeout(5000)).call("firstExpectedArg");
    verify(callback, timeout(5000)).call("expectedArg");
    // no need to tell more, if additional calls come after the expected arg
    // verify(callback, timeout(5000)).call("randomArg");
}