简体   繁体   English

使用 Mockito 对 Runnable 进行单元测试

[英]Unit test for Runnable with Mockito

I have a code like this for which I would like to write unit test.我有一个这样的代码,我想为其编写单元测试。

public class TestClass {

private final Executor executor;
private final Handler handler;

TestClass(Executor executor, Handler handler) {
    this.executor = executor;
    this.handler = handler;
}

void doSomething(String param1) {
    executor.execute(new Runnable() {
        @Override
        public void run() {
            //do something
            handler.callHandler();
        }
    });
}
}

How can I use Mockito / Powermockito to verify if the callHandler() method is invoked.如何使用 Mockito / Powermockito 来验证 callHandler() 方法是否被调用。

Pass a mock Handler to the constructor of TestClass . 将模拟Handler传递给TestClass的构造函数。

Then use Mockito.verify() to assert that callHandler() method was called. 然后使用Mockito.verify()断言调用了callHandler()方法。

Involving concurrency 涉及并发

You can stub an answer that counts down on a CountDownLatch to make the test wait for the handler to be hit. 您可以存储一个在CountDownLatch上倒计时的答案,以使测试等待处理程序被命中。 Waiting will involve setting a reasonable timeout, which can be tricky, you don't want it too high, or failure will make the test run much longer, and not too low so that you don't get false positives. 等待将涉及设置合理的超时,这可能会非常棘手,你不希望它太高,或失败会使试运行更长的时间,而不是太低了,这样你就不会得到误报。

Handler handler = mock(Handler.class);
CountDownLatch finished = new CountDownLatch(1);

doAnswer(invocation -> {
    finished.countDown();
    return null;
}).when(handler).callHandler();

TestClass testClass = new TestClass(executor, handler);

testClass.doSomething("thisThing");

boolean ended = finished.await(10, TimeUnit.SECONDS);

assertThat(ended).isTrue();

verify(handler).callHandler();

Bypassing concurrency 绕过并发

If you're only trying to determine whether the handler is invoked you can use an Executor that executes on the same thread. 如果您只是尝试确定是否调用了处理程序,则可以使用在同一线程上执行的Executor This will make for a more stable test. 这将使测试更加稳定。

Handler handler = mock(Handler.class);
Executor executor = new Executor() {
    @Override
    public void execute(Runnable command) {
        command.run();
    }
};

TestClass testClass = new TestClass(executor, handler);

testClass.doSomething("thisThing");

verify(handler).callHandler();

Another way you can handle the concurrency issue is to mock the Executor to "do nothing" when called and use an ArgumentCaptor in your test to capture the Runnable it would have invoked.处理并发问题的另一种方法是模拟 Executor 在调用时“什么都不做”,并在测试中使用ArgumentCaptor来捕获它会调用的 Runnable。 Once you have the Runnable you can manually invoke it in the same thread as your test.拥有 Runnable 后,您可以在与测试相同的线程中手动调用它。

Here's an example:下面是一个例子:

@Mock
private Executor executor;
@Mock
private Handler handler;

@Before
public void setup() {
    openMocks(this);

    doNothing().when(executor).execute(any());
}

@Test
public void runTest() {
    TestClass testClass = new TestClass(executor, handler);
    testClass.doSomething("the thing");

    ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
    verify(executor).execute(runnable.capture());
    Runnable capturedRunnable = runnable.getValue();
    capturedRunnable.run();

    verify(handler).callHandler();
}

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

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