我如何很好地对异步方法进行单元测试?

sor*_*ist 5 java junit unit-testing asynchronous

我目前正在使用线程锁定对我的异步方法进行单元测试,通常我将 aCountDownLatch注入我的异步组件并让主线程等待它达到 0。但是,这种方法看起来很丑陋,并且不能很好地扩展,考虑一下当我为一个组件编写 100 多个测试并且它们都必须按顺序等待工作线程执行一些假异步作业时会发生什么。

那么还有另一种方法吗?考虑以下简单搜索机制的示例:

搜索器.java

public class Searcher {

    private SearcherListener listener;

    public void search(String input) {
        // Dispatch request to queue and notify listener when finished
    }

}
Run Code Online (Sandbox Code Playgroud)

搜索者监听器.java

public interface SearcherListener {

    public void searchFinished(String[] results);

}
Run Code Online (Sandbox Code Playgroud)

您将如何在search不使用多个线程并阻止一个线程等待另一个线程的情况下对该方法进行单元测试?我从How to use Junit to test asynchronous processes 中汲取了灵感,但最重要的答案没有提供具体的解决方案来说明这将如何工作。

Ale*_*ien 1

为异步编写单元测试看起来从来都不是很好。

testMyAsyncMethod()在您准备好检查正确的行为之前,(主线程)必须阻塞。这是必要的,因为测试用例在方法结束时终止。所以没有办法,问题只是你如何阻止。

一种对生产代码影响不大的简单方法是使用 while 循环:假设 AsyncManager 是被测试的类:

ArrayList resultTarget = new ArrayList();
AsyncManager fixture = new AsyncManager(resultTarget);
fixture.startWork();
// now wait for result,  and avoid endless waiting
int numIter = 10;
// correct testcase expects two events in resultTarget
int expected = 2;
while (numIter > 0 && resulTarget.size() < expected) {
   Thread.sleep(100);
   numIter--;
}
assertEquals(expected, resulTarget.size());
Run Code Online (Sandbox Code Playgroud)

高效的代码将在 AsyncManager 的构造函数中使用适当的目标或使用另一个构造函数。出于测试目的,我们可以通过我们的测试目标。

您只需为固有的异步任务(例如您自己的消息队列)编写此代码。对于其他代码,只统一执行计算任务的类的核心部分(特殊算法等),不需要让它在线程中运行。

然而,对于您的搜索侦听器,所示的循环和等待原理是合适的。

public class SearchTest extends UnitTest implements SearchListener {
  public void searchFinished() {
     this.isSearchFinished = true;
  }

  public void testSearch1() {
    // Todo setup your search listener, and register this class to receive 
    Searcher searcher = new Searcher();
    searcher.setListener(this);
    // Todo setup thread
    searcherThread.search();
    asserTrue(checkSearchResult("myExpectedResult1"));

  }

  private boolean checkSearchResult(String expected) {
    boolean isOk = false;
    int numIter = 10;
    while (numIter > 0 && !this.isSearchFinished) {
       Thread.sleep(100);
       numIter--;
    }
    // todo somehow check that search was correct
    isOk = .....

    return isOk;
  }
}
Run Code Online (Sandbox Code Playgroud)