10

我正在尝试测试一种在单独的线程中工作的方法,简化如下:

public void methodToTest()
{
    Thread thread = new Thread()
    {
        @Override
        public void run() {
            Clazz.i = 2;
        }
    };
    thread.start();
}

在我的单元测试中,我想测试 Clazz.i == 2,但我不能这样做,因为我认为断言是在线程更改值之前运行的。我想用另一个线程来测试它,然后用 join 等待,但它仍然不起作用。

SSCCE:

@Test
public void sscce() throws InterruptedException
{
    Thread thread = new Thread()
    {
        @Override
        public void run() {
            methodToTest()
        }
    };
    thread.start();     
    thread.join();  
    AssertEquals(2, Clazz.i);
}

public static class Clazz
{
    public static int i = 0;
}

我认为这是因为测试主代码创​​建了一个正在等待(加入)到第二个线程的线程,但是第二个线程没有完成工作,它创建了另一个线程来完成工作然后完成,这继续第一个线程,而第三个线程Clazz.i = 2在断言之后执行。

我怎样才能使第一个线程等待它启动的线程以及该线程启动的任何线程

4

3 回答 3

5

如果没有对 中创建的线程的引用methodToTest,您就不能,很简单。Java 没有提供用于查找“在此特定时间段内产生的线程”的机制(即使它提供了,它也可以说是一种难看的机制)。

在我看来,你有两个选择:

  • methodToTest等待它产生的线程。当然,如果您明确希望这是一个异步操作,那么您就不能很好地做到这一点。
  • 从 中返回新创建的线程methodToTest,这样任何调用者都可以选择等待它,如果他们愿意的话。

可以注意到,第二选择可以用几种不同的方式来表述。例如,Future如果您想扩展methodToTest使用各种方式进行异步工作的自由,您可以返回一些类似抽象的对象而不是线程。您也许还可以定义一些全局任务池,强制所有异步任务在其中运行,然后等待池中的所有任务完成,然后再检查断言。这样的任务池可以采用ExecutorService、 或ThreadGroup、 或任何数量的其他形式。它们最终都做同样的事情,但可能或多或少适合您的环境 - 要点是您必须明确地让调用者访问新创建的线程,这是某种方式。

于 2016-01-29T02:42:13.567 回答
3

由于您的线程似乎正在执行不同的操作,您可以使用CountDownLatch来解决您的问题。

在主线程中声明一个CountDownLatch并将这个闩锁对象传递给其他线程。在主线程中使用 await() 并在其他线程中减少闩锁。

在主线程中:(第一个线程)

CountDownLatch latch = new CountDownLatch(2);
/* Create Second thread and pass the latch. Pass the same latch from second 
   thread to third thread when you are creating third thread */
try {
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}

将此闩锁传递给第二个和第三个线程并在这些线程中使用倒计时

在第二和第三线程中,

try {
    // add your business logic i.e. run() method implementation
    latch.countDown();
} catch (InterruptedException e) {
    e.printStackTrace();
}

看看这篇文章以获得更好的理解。

ExecutorService invokeAll () API 是其他更好的解决方案。

于 2016-01-29T03:30:22.203 回答
0

您不能对单元不提供的功能进行单元测试。

你是说你想验证methodToTest() 最终设置Clazz.i=2,但“最终”是什么意思?您的methodToTest()函数不向其调用者提供任何知道何时Clazz.i设置的方法。您很难弄清楚如何测试该功能的原因是您的模块不提供该功能。

这可能是您阅读测试驱动开发 (TDD) 的好时机。那是您首先编写测试的地方,然后编写使测试通过的代码。首先编写测试可以帮助您更清晰地描绘您希望模块执行的任何操作。

它还有一个附带的好处:如果您执行严格的TDD(即,如果您从不编写任何模块代码,只是为了通过测试),那么您的测试覆盖率将是 100%。

而且,这带来了另一个好处:如果你有 100% 的测试覆盖率,那么你可以毫无畏惧地重构,因为如果你破坏了任何东西,你的单元测试会告诉你。

于 2016-01-29T15:21:12.853 回答