问题 如何单元测试回调逻辑?


更完整的问题是,给定一个期望回调作为参数的依赖项,如何编写涵盖回调逻辑的单元测试  仍设法模拟依赖?

public class DoStuff {
    public void runThis(Runnable callback) {
        // call callback
    }
}

public class ClassUnderTest {
    private DoStuff stuffToDo;

    public void methodUnderTest() {
        this.stuffToDo.runThis(/*a runnable with some logic*/)
    }
}

在上面的例子中,我会嘲笑 stuffToDo 因为我应该验证方法调用的调用和模拟输出。然而,嘲笑 runThis 导致回调逻辑未被测试。此外,回调逻辑似乎应该是私有的,所以我不希望直接测试它;或许这对我来说是个误解。

由于回调被相当广泛地使用,我希望有一种常用的方法来测试它们,但我还没有找到它。


5176
2017-08-22 13:16


起源

回调是否只需要在此上下文中进行测试?如果是这样,为什么不简单地为你计划通过的各种回调编写测试,这表示你对结果的期望?我错过了什么吗? - Jonah
您是否尝试过使用此示例中的CountDownLatch: stackoverflow.com/a/3802487/2301224 - Baker


答案:


你不能在一次测试中。如果你嘲笑某事,那就是 嘲笑,这意味着它只能验证参数并模拟返回值(在测试中配置)。

实际上,模拟某事的全部意义在于测试单独使用模拟的内容。如果你想进行单元测试 DoStuff,您不必担心使用某些可能会或可能不会正常工作的回调实现。你嘲笑回调,所以你不必担心它。

通过单独测试回调代码,您仍然可以进行良好的测试,  单独测试回调用户(使用mock作为回调),并且可能通过抛出集成测试进行良好测量,其中您将完全配置的组件作为一个整体使用。


10
2017-08-22 13:31





这里基本上你想测试这个类 做东西。这意味着您需要对DoStuff中的所有方法进行彻底测试,对吧?所以在这种情况下,你需要做的是,而不是嘲笑 stuffToDo 本身, 注入 一个模拟的Runnable into stuffToDo。然后检查您的runnable是否成功执行。

但是如果你在课堂上有其他功能,你可以进行单独测试并模拟它们。


1
2017-08-22 13:32



我想测试ClassUnderTest,它将DoStuff作为依赖项。 - Andrew White


在您的特定情况下,听起来您已经测试过了 DoStuff (或者不再关心,因为它被嘲笑)现在特别是对特定的单元测试 Runnable 你设计的。在这种情况下, callback 听起来就像你想要测试的一样,有人可能想要直接对数据库策略或内存策略进行单元测试。

如果这是你正在尝试的,你要么可以在一个黑盒子里测试它,然后再进行测试 ClassUnderTest 尽你所能。或者,您可以在特定的Runnable上创建测试工具。如果要发布此代码并且不希望使测试工具可访问,则可以将测试工具方法设为私有,并专门与单元测试程序集共享其详细信息。

这里 有关如何制作朋友集会的信息。我通常签署我的单元测试代码,以便我不必使用命令行编译器。我想这取决于你的构建环境。


0
2017-08-22 13:42



不,ClassUnderTest是要测试的类。我在嘲笑DoStuff。 - Andrew White
我修改了类名来反映这一点。它不会影响答案。 - Michael Hays


如果只是无条件地调用Runnable的假DoStuff怎么样?

然后你只需要感知效果 - 如果你的回调被执行就应该观察到的变化。


0
2017-08-23 05:40





如果您使用EasyMock,则可以使用 andStubAnswer 调用runnable。

doSomethingMock.runThis(runnable);
expectLastCall().andStubAnswer(new IAnserable<Void>() {
   Runnable runnable = (Runnable)getCurrentArguments()[0];
   runnable.run();
   return null;
});

我想其他的模拟框架包含类似的东西。


0
2017-08-22 19:45