8

作为一名 TDD 从业者,我想测试我编写的所有代码。

在过去的几年里,我一直在编写许多多线程代码,并且我的一部分测试非常麻烦。

当我必须断言在r ​​un()循环期间可能发生的事情时,我最终会得到一些类似这样的断言:

assertEventually(timeout, assertion)

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

所以我的问题是,有人知道提供这个的图书馆吗?

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

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

4 回答 4

5

您可能想查看两个库:

  • ConcurrentUnit - 允许您从任何线程执行断言,等待固定的时间
  • 等待- 允许您等待固定的时间或直到满足某些条件
于 2013-08-30T20:05:31.843 回答
1

从您使用的语言 -断言某事可能是真的- 听起来您正在走上一条会导致脆弱测试的道路。测试应该有二元结果——它们要么通过,要么失败,并且没有灰色区域。

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

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

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

于 2013-08-15T10:42:58.827 回答
1

如果你来自 Scala,你可能已经习惯了finally trait。

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

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

于 2017-10-09T11:04:03.443 回答
0

及时性测试是一种性能测试,除非您有严格的实时要求,否则它会通过许多样本的统计数据来断言。

单元测试只关注单个样本,它们应该是确定性的。我认为您应该通过某种机制通知您的 JUnit 线程,而不是这种检查和循环构造,只要您想要断言的事件发生。Callbacks、Futures、Latches、Exchangers 等可以用来构建这样的机制。

构建多线程代码是一个设计挑战,这样它就可以在没有检查循环或状态自旋等待的情况下进行测试。

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

于 2013-08-15T12:26:27.970 回答