【问题标题】:Unit testing a nonblocking method (asynchronous testing)对非阻塞方法进行单元测试(异步测试)
【发布时间】:2012-07-11 14:47:42
【问题描述】:

我有一个非常简单的Checkclass,它有一个阻塞waitForCondition() 方法。这个方法是阻塞的。我想为此方法创建一些单元测试。首先,该方法应在满足条件时返回。其次,方法应该在被中断时返回。

Check 类在内部有一个ArrayBlockingQueue 并调用它的take() 方法,所以我的测试实际上是关于正确编码条件的逻辑(应该如此)。 在应用程序中,Check 类的数据由另一个线程通过InputData 方法提供。 InputData 方法对传入的数据执行逻辑,并在满足条件时在 ArrayBlockingQueue 中放置一个虚拟对象。这应该会导致 waitForCondition() 返回。

所以我的第一个想法是我可以通过模拟来测试InputData,并检查当条件满足时虚拟对象是否被添加到队列中。这将需要更改类的设计,因为队列是私有数据成员(除非可以模拟私有数据)。而不是InputData在满足条件时直接添加到队列中,它必须调用一些可以模拟的东西。

但是如果InputData 工作正常,那么检查waitForCondition() 方法本身就会出现问题。真的很简单的代码:

try {
        myArrayBlockingQueue.take();
        return true;
    } catch (InterruptedException ex) {
        return false;
    }

所以我想知道是否值得想象的麻烦:一个测试用Check 创建另一个线程,调用它的waitForCondition(),然后在完成后返回一些东西。也许,使用 Executor 服务。模糊的部分是如何同步assertTrue(...)。我发现了这个article on asynchronous testing,看起来它可以解决问题。

问题总结:

  1. 我应该更改设计以测试InputData() 中的逻辑吗?如果可以,应该怎么做?
  2. 只要测试了InputData(),我是否应该忽略waitForCondition() 的测试?
  3. 还是只做需要做的事情(有点复杂的单元测试)并直接测试waitForCondition() 更好?

【问题讨论】:

    标签: java multithreading unit-testing


    【解决方案1】:

    如果你在 Check 类的构造函数中注入了 ArrayBlockingQueue 的实例,那么你的测试可以在测试中间注入适当的值。

    然后可以超时运行单元测试,如果在 100ms 左右没有返回则失败。

    【讨论】:

    • +1 有趣的想法。可以模拟注入的 Queue 在某些测试条件下抛出 InterruptedException,等待很长时间等......
    • 好主意。我向Check 类添加了一个构造函数,用于测试目的,它按照您的建议注入队列。然后,我能够通过直接设置和读取实际队列(不是模拟)来测试所有公共方法。我只需要使用一个模拟来测试 InterruptedException。使用 Mockito:when(queueMock.take()).thenThrow(new InterruptedException()); 因此,在这种情况下,我们可以在没有任何异步/时序问题的情况下进行测试。
    • 如果阻塞对象的数量很少,并且您的代码是结构化的,因此它们可以被注入,这是一个很好的解决方案。
    【解决方案2】:

    感谢您提供的精美链接!我遇到了一些类似的问题,也许这个链接比我做的更好。 (我也很想看看这个问题还有哪些其他答案——这是个好问题)

    如果您不介意更改(至少暂时)您的实际代码(是的,这不是通常的单元测试实践!)您可以做一些我称之为"Error Injection" 的事情。

    在我的实现中,您创建了一个从属性(或地图)中读取的类,以在特定的独特点“做一些有趣的事情”。例如你的属性可能会说

    myClass.myMethod.blockingQueueTake = interrupt:
    myClass.myLongCalculation = throw: java.lang.ArithmeticException(Failed to converge)
    

    在您的代码中,添加测试行,例如在您的queue.take() 之前,添加

    TestSimulator.doCommand("myClass.myMethod.blockingQueueTake");
    

    优点是一切都发生在真实的代码中,而不是一些 Mock,它会变得非常毛茸茸。 (在我的情况下,SW 较旧,不是为单元测试编写/设计的,因此制作 Mock 非常困难)缺点是您可能想在之后删除或注释掉代码。所以它真的不是一个持续集成类型的单元测试,它更像是一次非常严肃的现实调试。所以,我承认,它远非理想,但是,它确实为我找到了一堆错误!

    【讨论】:

      【解决方案3】:

      您还可以使用“测试运行器”类在循环中运行断言。该循环将在 try/catch 中运行断言。异常处理程序将简单地尝试再次运行断言,直到超时到期。我最近写了一篇关于这种技术的博客文章。这个例子是用 groovy 写的,但是这个概念应该很容易适应 Java。

      http://www.greenmoonsoftware.com/2013/08/asynchronous-functional-testing/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-13
        • 2013-03-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多