【问题标题】:Unit test for Runnable with Mockito使用 Mockito 对 Runnable 进行单元测试
【发布时间】:2017-11-22 21:24:55
【问题描述】:

我有这样的代码,我想为其编写单元测试。

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

如何使用 Mockito / Powermockito 来验证 callHandler() 方法是否被调用。

【问题讨论】:

  • 我其实不喜欢doSomething的方法。您在这里遇到的问题是此方法做了两件事:创建 Runnable 并让它由执行程序执行。我会重构该方法以接收已经构建的 Runnable,因此它只调用 executor.execute(runnable);,然后测试它会容易得多。

标签: java unit-testing mocking mockito powermockito


【解决方案1】:

将模拟 Handler 传递给 TestClass 的构造函数。

然后使用Mockito.verify() 断言调用了callHandler() 方法。

涉及并发

您可以存根一个在CountDownLatch 上倒计时的答案,以使测试等待处理程序被命中。等待将涉及设置一个合理的超时时间,这可能很棘手,您不希望它太高,否则失败会使测试运行时间更长,也不能太低,以免出现误报。

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

绕过并发

如果您只是想确定是否调用了处理程序,您可以使用在同一线程上执行的Executor。这将使测试更加稳定。

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

【讨论】:

  • 当测试方法到达verify(handler).callHandler();时,你可以确保执行器已经运行了Runnable
  • 啊,这是并发问题,不是 Mockito 问题?
  • This 应该有助于解决并发问题。
  • 好吧,至少两者都有,我会说。执行器可能很棘手,因为我们无法确定它何时实际运行该过程:((我不知道任何方法可以知道)
  • 如果测试只是试图断言处理程序被调用,它可能最容易提供在同一线程上执行的 Executor。
【解决方案2】:

处理并发问题的另一种方法是模拟 Executor 在调用时“什么也不做”,并在测试中使用 ArgumentCaptor 来捕获它会调用的 Runnable。拥有 Runnable 后,您可以在与测试相同的线程中手动调用它。

这是一个例子:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    • 2016-03-15
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    相关资源
    最近更新 更多