如何在另一个线程中进行代码结果的jUnit测试

15

我有一个在线程中运行的进程(用作实时信号分析进程)。我想给这个线程进程提供已知输入,然后在jUnit中测试,以确保输出正确。我有一个回调监听器,当线程完成处理数据时可以通知我,我可以通过将测试用例本身注册为侦听器,在结果上成功运行断言。

当这些断言失败时,它们会抛出异常。但是由于它们发生在测试方法之外,jUnit没有将此异常注册为失败。

我该如何结构化我的jUnit测试,以便在侦听器返回后正确地失败测试?以下是代码的简化版本。

 public class PitchDetectionTest extends TestCase 
    implements EngineRunCompleteListener() {
  AudioData            fixtureData;
  PitchDetectionEngine pitchEngine;

  public void setUp() {
    fixtureData = <stuff to load audio data>;
  }

  public void testCorrectPitch() {
    pitchEngine = new PitchEngine(fixtureData);
    pitchEngine.setCompletionListener(this);
    pitchEngine.start();   
    // delay termination long enough for the engine to complete
    try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
  }

  // gets called by the PitchEngine when it has finished processing all data in the
  // fixtureData.   This is the only method defined by interface    
  // EngineRunCompleteListener.
  public void notifyEngineRunComplete() {

    // The real code asserts things about the PitchEngine's results.   When they fail, 
    // an exception is thrown that I can see in the console, but this unit test still  
    // shows as 'success' in the jUnit interface.   What I want is to see 
    // testCorrectPitch() fail.
    assertTrue(false);  

  }

}

public class PitchEngine () {
  EngineRunCompleteListener completionListener();
  Thread myThread;

  public void start() {
    // other stuff
    myThread = new Thread(this);
    myThread.start();    
  }

  public void run() {
    while (some condition) {
      // do stuff with the data
    }
    if (engineRunCompleteListener != null) {
      engineRunCompleteListener.notifyEngineRunComplete();
    }
  }

}

这可能很有用 - http://code.google.com/p/awaitility/ - Premraj
3个回答

11

您已经有两个线程在运行,即JUnit线程和由myThread.start()启动的进程线程。
我能想到至少两种选项,它们都涉及将断言从notifyEngineRunComplete中移出。例如:

  • 您可以使用join等待进程线程完成,然后进行断言(Javadoc 在这里)。
  • 您可以通过等待监视器对象来使您的junit线程休眠,并且在回调函数中通知此监视器。这样,您就会知道进程已经完成了。
  • 您可以使用ExecutorFuture对象。如果它适用于您的类,则我认为这将是最酷的解决方案(Javadoc 在这里)。

我们最终采用了join()方法。我不得不向引擎类添加一个方法来返回其线程,这感觉有点不好,因为这意味着为了测试它们而向类添加方法,但除此之外非常简单且有效。感谢您的建议。 - IdahoEv
你可以在引擎类中添加一个名为joinMe()的方法或类似的方法,以执行相同的功能。这样,单元测试线程就可以等待引擎完成而不知道其内部线程。 - Yoni
你还应该考虑使用AtomicBoolean来标记在另一个线程中发生的情况,并在主线程中的join之后断言其值。 - Snicolas
@yoni,我采用了“ExecutorService”和“FutureTask”的方法。我必须说这是正确的方法,但是这段代码并不容易阅读。这绝对是正确的方法,但下一个问题/调查我将要做的是弄清楚我们是否可以/值得清理这段代码。 - Elliott

1
我想给那个线程进程提供一个已知的输入,然后在 jUnit 中测试输出是否正确。我有一个回调监听器,可以在线程完成数据处理时通知我,并且我可以通过将测试用例本身注册为侦听器来成功地对结果运行断言。
与其在单元测试中启动一个单独的线程来处理 PitchEngine,为什么不将 PitchEngine 中的“//do stuff with the data”逻辑提取到另一个公共方法中,并在单元测试中简单地调用它呢?我想不出任何原因在单元测试中实际生成线程,因为在这个单元测试中,你真正关心的是测试处理逻辑。

这是一个很好的想法,就像我在这里展示的简化版本一样。但遗憾的是,在这种情况下,在引擎中运行“做事情”涉及连接一堆数据处理对象并管理它们之间的交互。它依赖于PitchEngine中的几种方法以及其超类中的几种方法,并且不容易提取。我可以看到改进模块化的一些方法,但它们需要比我现在拥有的时间更多的时间。 - IdahoEv
你有考虑在 PitchEngineTest 中使用模拟对象来替换那些其他对象吗? - matt b
在这种情况下使用模拟对象是没有用的;我尝试编写的测试是针对pitch引擎的全栈测试,确认它在给定样本音频波形时是否检测到了正确的pitch。我需要运行所有实际的代码。 - IdahoEv

1

如果必须在回调函数中运行断言代码,请在try catch中包装回调方法。捕获任何可抛出的异常并有一种将该异常传递回junit线程的方法(某种共享线程状态)。然后junit线程只需重新抛出任何返回的throwable。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接