【问题标题】:Junit test the correct number of threads has startedJunit测试正确的线程数已经开始
【发布时间】:2012-07-25 04:16:47
【问题描述】:

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

【问题讨论】:

    标签: java multithreading unit-testing


    【解决方案1】:

    与其编写自己的方法来启动线程,不如使用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));
    }
    

    请注意,所有这些代码只是我尚未测试甚至尝试编译的草图。但它应该让您了解代码的结构。

    【讨论】:

    • 请参阅下面我对@Alex D 的回复。能举个例子吗?
    • @kasavbere 请参阅javadoc of ExecutorService,它提供了一些示例。
    【解决方案2】:

    与其说你要“测试五个线程是否已经启动”,倒不如退一步想想这五个线程实际上应该做什么。然后进行测试以确保“某事”实际上正在完成。

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

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

    针对您的评论的更多想法

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

    无论您对此做什么,我都会肯定测试在多线程模式下运行时任务是否正确完成。根据个人经验,我可以告诉您,一旦您开始使用线程远程执行任何复杂的任何事情,就很容易得到仅在特定条件下出现的细微错误,也许只是偶尔。处理多线程代码的复杂性是一个很滑的斜坡。

    因此,如果你能做到,我会强烈建议你做的不仅仅是简单的单元测试。在使用多线程、多核机器、非常大的数据集运行任务的情况下进行压力测试,并确保所有答案都完全符合预期。

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

    【讨论】:

    • 五个线程用于加速一个线程可以完成的进程。想象一下,让五个远程机器人做某事,然后报告回来。我的测试环境使用虚拟数据,所以isAlive 不能保证工作。此外,检查结果不会显示任何内容,因为 [生产环境中] 唯一明显的变化就是速度。
    猜你喜欢
    • 2011-08-05
    • 1970-01-01
    • 1970-01-01
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 1970-01-01
    • 2011-08-22
    相关资源
    最近更新 更多