单元测试必须手动中断的异步计算

时间:2017-02-27 20:49:15

标签: java multithreading unit-testing asynchronous

我有一个以异步方式记录眼球跟踪数据的类。记录过程有startstop方法。数据收集在一个集合中,只有在录制线程完成其工作时才能访问该集合。它基本上封装了所有线程和同步,因此我的库的用户不必这样做。

严重缩短的代码(遗漏了泛型和错误处理):

public class Recorder {
  private Collection accumulatorCollection;
  private Thread recordingThread;

  private class RecordingRunnable implements Runnable {
    ...

    public void run() {
      while(!Thread.currentThread().isInterrupted()) {
        // fetch data and collect it in the accumulator
        synchronized(acc) { acc.add(Eyetracker.getData()) }
      }
    }
  }

  public void start() {
    accumulatorCollection = new Collection();
    recordingThread = new Thread(new RecordingRunnable(accumulatorCollection));
    recordingThread.start();
  }

  public void stop() {
    recordingThread.interrupt();
  }

  public void getData() {
    try {
      recordingThread.join(2000);
      if(recordingThread.isAlive()) { throw Exception(); }
    }
    catch(InterruptedException e) { ... }

    synchronized(accumulatorCollection) { return accumulatorCollection; }
  }
}

用法非常简单:

recorder.start();
...
recorder.stop();
Collection data = recorder.getData();

我对整个事情的问题是如何测试它。目前我这样做:

recorder.start();
Thread.sleep(50);
recorder.stop();
Collection data = recorder.getData();
assert(stuff);

这是有效的,但它是非确定性的并且会使测试套件变得相当慢(我将这些测试标记为集成测试,因此必须单独运行以避免此问题)。

有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

使用CountDownLatch有更好的方法。

测试的非确定性部分源于您未考虑的两个变量:

  • 创建和启动一个线程需要花费时间,当Thread.start()返回时,线程可能还没有开始执行runnable(runnable将被执行,但可能会稍晚一些)。
  • 停止/中断会破坏Runnable中的while循环但不会立即中断,可能会稍晚。

这是CountDownLatch的用武之地:它为您提供有关另一个线程执行位置的准确信息。例如。让第一个线程等待锁存器,而第二个线程倒计时#34; latch作为runnable中的最后一个语句,现在第一个线程知道runnable已完成。 CountDownLatch也充当同步器:无论第二个线程写入内存,现在都可以被第一个线程读取。

您也可以使用volatile布尔值,而不是使用中断。任何读取volatile变量的线程都可以保证看到任何其他线程设置的最后一个值。

还可以给CountDownLatch一个超时,这对于可以挂起的测试很有用:如果你需要等待很长时间,你可以中止整个测试(例如关闭执行器,中断线程)并抛出{{ 1}}。在下面的代码中,我重新使用超时来等待收集一定数量的数据而不是“睡觉”。

作为优化,使用Executor(ThreadPool)而不是创建和启动线程。后者相对昂贵,使用Executor可以真正发挥作用。

在更新后的代码下面,我将其作为应用程序(AssertionError方法)运行。 (编辑28/02/17:在while循环中检查maxCollect> 0)

main

快乐编码:)