用于在Java中断言多线程代码的库

时间:2013-08-14 19:16:13

标签: java multithreading unit-testing

作为TDD从业者,我想测试我编码的所有内容。

在过去的几年里,我一直在编写许多多线程代码,而且我的测试中有一部分一直困扰着。

当我必须断言在 run()循环期间可能发生的事情时,我最终会得到某种类似的断言:

assertEventually(timeout, assertion)

我知道Mockito有一个解决方案,但仅适用于验证调用。我也知道 JUnit 有一个超时属性,可以避免挂起(或永久)测试。但我想要的是能够让我断言可能随着时间的推移变成真实的东西。

所以我的问题是,有没有人知道提供此功能的库?

到目前为止,这是我的解决方案:

private void assertEventually(int timeoutInMilliseconds, Runnable assertion){
    long begin = System.currentTimeMillis();
    long now = begin;
    Throwable lastException = null;
    do{
        try{
            assertion.run();
            return;
        }catch(RuntimeException e){
            lastException = e;
        }catch(AssertionError e){
            lastException = e;
        }
        now = System.currentTimeMillis(); 
    }while((now - begin) < timeoutInMilliseconds);
    throw new RuntimeException(lastException);
}

使用它最终会像这样:

assertEvetuallyTrue(1000, new Runnable() {
        public void run() {
            assertThat(Thread.activeCount()).isEqualTo(before);
        }
    });

4 个答案:

答案 0 :(得分:3)

您可能想要查看的两个图书馆:

  • ConcurrentUnit - 允许您从任何线程执行断言,等待一段固定的时间
  • Awaitility - 允许您等待一段固定的时间或直到满足某些条件

答案 1 :(得分:1)

从你使用的语言 - 断言某些事情可能是真的 - 听起来你走的路会导致脆弱的测试。测试应该有二元结果 - 它们要么通过要么失败,而且没有灰色区域。

看一下这个具体的例子,考虑到使用多线程,我会建议这样的事情:

  • 重构您的设计,以便代码中的所有实际逻辑都可以独立于多线程环境独立运行,作为同步执行的代码,这应该使编写单元测试相当简单。
  • 在所有这些代码之外实现您的线程管理 - 使用单元测试进行测试会更加困难,因此您应该将其与代码的其余部分完全隔离,以免其他代码变得更难测试
  • 开始构建一个高级系统测试套件,作为一个整体执行系统(或其大部件),通过应用程序的外部边界驱动测试。这些将覆盖线程处理代码,并测试系统中各种组件的集成。此外,您不必编写处理线程的特定测试逻辑 - 它们应该在您测试时在系统内部运行。

以这种方式拆分测试的另一个优点是,您可以为单元测试和系统测试创建单独的测试套件,这有助于保持您的单元测试套件快速且精简(因此您可以更轻松,更经常地运行它)发展)。任何涉及超时的测试(即本例中的系统测试)都需要更长的时间来执行,因此更适合仅偶尔运行 - 这种方法使这更容易。

答案 2 :(得分:1)

如果您来自Scala,您可能会习惯于eventually特征。

它对测试非常有用,因为您可以重试一段时间,直到最终返回结果为止。

这里有 halfvim 制作的Java包装器:https://github.com/halfvim/eventually

答案 3 :(得分:0)

及时性测试是一种性能测试,除非您有实时性要求,否则会对许多样本进行统计。

单元测试只关注单个样本,它们应该是确定性的。而不是这个检查和循环结构,我认为你应该通过某种机制通知你的JUnit线程,只要你想要断言的事件发生。回调,期货,锁存,交易所等可以用来建立这样的机制。

构建多线程代码是一项设计挑战,因此可以在没有检查和循环的情况下对其进行测试,或者在状态上进行旋转等待。

但是如果你真的必须这样做,那么使用System.nanoTime()代替System.currentTimeMillis()因为它更频繁地更新,尽管两个时钟的实际实现和准确性取决于你的JVM版本,操作系统和硬件。