Junit测试已启动的正确线程数

时间:2012-07-25 04:16:47

标签: java multithreading unit-testing

所以我有一个启动五个线程的方法。我想编写一个单元测试来检查五个线程是否已经启动。我怎么做?非常感谢示例代码。

2 个答案:

答案 0 :(得分:5)

为什么不使用可以注入到类中的Executor,而不是编写自己的方法来启动线程?然后,您可以通过传入一个虚拟Executor轻松测试它。

修改:以下是一个关于如何构建代码的简单示例:

public class ResultCalculator {
    private final ExecutorService pool;
    private final List<Future<Integer>> pendingResults;

    public ResultCalculator(ExecutorService pool) {
        this.pool = pool;
        this.pendingResults = new ArrayList<Future<Integer>>();
    }

    public void startComputation() {
        for (int i = 0; i < 5; i++) {
            Future<Integer> future = pool.submit(new Robot(i));
            pendingResults.add(future);
        }
    }

    public int getFinalResult() throws ExecutionException {
        int total = 0;
        for (Future<Integer> robotResult : pendingResults) {
            total += robotResult.get();
        }
        return total;
    }
}

public class Robot implements Callable<Integer> {
    private final int input;

    public Robot(int input) {
        this.input = input;
    }

    @Override
    public Integer call() {
        // Some very long calculation
        Thread.sleep(10000);

        return input * input;
    }
}

以下是您从main()

中调用它的方式
public static void main(String args) throws Exception {
    // Note that the number of threads is now specified here
    ExecutorService pool = Executors.newFixedThreadPool(5);
    ResultCalculator calc = new ResultCalculator(pool);
    try {
        calc.startComputation();
        // Maybe do something while we're waiting
        System.out.printf("Result is: %d\n", calc.getFinalResult());
    } finally {
        pool.shutdownNow();
    }
}

以下是你如何测试它(假设JUnit 4和Mockito):

@Test
@SuppressWarnings("unchecked")
public void testStartComputationAddsRobotsToQueue() {
    ExecutorService pool = mock(ExecutorService.class);
    Future<Integer> future = mock(Future.class);
    when(pool.submit(any(Callable.class)).thenReturn(future);

    ResultCalculator calc = new ResultCalculator(pool);
    calc.startComputation();

    verify(pool, times(5)).submit(any(Callable.class));
}

请注意,所有这些代码只是一个草图,我还没有测试过,甚至还没有尝试编译。但它应该让您了解如何构建代码。

答案 1 :(得分:4)

不要说你要“测试五个线程是否已经开始”,而是最好退一步考虑五个线程实际上应该做什么。然后测试以确保实际上正在做“某事”。

如果您真的只想测试线程是否已经启动,那么您可以做一些事情。你是否在某处保留对线程的引用?如果是这样,您可以检索引用,计算它们,并在每个引用上调用isAlive()(检查它是否返回true)。

我相信在某些Java平台类上有一些方法,您可以调用它来查找正在运行的线程数,或查找ThreadGroup中运行的所有线程,但您必须搜索到找出它是什么。

回应您的评论的更多想法

如果您的代码与new Thread(runnable).start()一样简单,我就不会费心去测试线程是否真正启动了。如果你这样做,你基本上只是测试Java平台的工作原理(确实如此)。如果用于初始化和启动线程的代码更复杂,我会将thread.start()部分存根,并确保使用正确的参数调用存根所需的次数等。

无论你做了什么,我都肯定测试在多线程模式下运行时任务是否正确完成。从个人经验来看,我可以告诉你尽快当你开始做任何远程复杂的线程时,很容易得到只在某些条件下出现的微妙错误,也许只是偶尔。处理多线程代码的复杂性是一个非常滑坡。

因此,如果你能做到这一点,我会高度建议你做的不仅仅是简单的单元测试。压力测试用多个线程,多核机器,非常大的数据集运行任务,并确保所有答案完全符合预期。

此外,尽管您希望使用线程提高性能,但我高度建议您使用不同数量的线程对程序进行基准测试,以确保实际实现所需的性能提升。根据系统的设计方式,可能会遇到并发性瓶颈,这可能会使您的程序在线程上比没有线程更快。在某些情况下,它甚至可以更慢