【问题标题】:JUnit terminates child threadsJUnit 终止子线程
【发布时间】:2011-02-19 16:22:01
【问题描述】:

当我测试创建子线程的方法的执行时,JUnit 测试在子线程之前结束并杀死它。

如何强制 JUnit 等待子线程完成执行?

【问题讨论】:

    标签: java unit-testing multithreading junit


    【解决方案1】:

    在阅读了问题和一些 cmets 之后,您似乎需要的是一种用于单元测试异步操作的技术。 doSomething() 立即返回,但您希望测试代码等待其完成,然后进行一些验证。

    问题是测试不知道调用产生的线程,所以显然它没有等待它们的方法。人们可以想出许多复杂(并且可能有缺陷)的方法来解决这个问题,但我认为这里存在设计问题。单元测试应该模拟一些 API 的客户端,并且它不应该假设任何关于实现的东西;它应该只测试 API 及其文档所反映的功能。因此,我会避免尝试检测和跟踪异步调用创建的线程。相反,如果需要,我会改进测试类的 API。异步调用所属的类应该提供一些检测终止的机制。我能想到 3 种方法,但可能还有更多:

    1. 允许注册一个在操作完成后得到通知的监听器

    2. 提供操作的同步版本。实现可以调用异步版本,然后阻塞直到完成。如果类不应该公开这样的方法,则可以将其可见性降低为包保护,以便测试可以访问它。

    3. 在某些可见对象上使用等待通知模式。

    如果类没有提供这样的机制,那么它就不是真正可测试的,更糟糕​​的是,它可能也不是很可重用。

    【讨论】:

      【解决方案2】:

      尝试在创建的线程上使用thread.join()。这将等待该线程终止。

      编辑:为避免这种情况,请在测试中尝试Thread.getThreadGroup().setDaemon(true);,或者在setUp() 方法中尝试。不过我还没有测试过。

      当最后一个线程停止或最后一个线程组被销毁时,守护线程组会自动销毁。

      不过,我想知道 JUnit 是否会在测试完成后立即调用 System.exit() 或其他什么。

      【讨论】:

      • 实际上我会避免这种解决方案,而是寻找一种更好的方法来运行这种测试并强制主线程等待其子线程结束。
      • @Marco:你能详细说明一下吗? Chris 的解决方案有什么问题?
      • 我正在测试一个方法 doSomething() ,它在内部启动一些更新一些数据库表的线程。我想做的是在我的 JUnit 测试中运行这个方法,然后检查数据库上的所有编辑是否都已成功保存。 doSomething() 方法在创建线程时不加入线程很重要,因此我不能采用 join() 解决方案。
      • 我建议创建一个线程组,创建一个新线程将其附加到该组并在其中创建新线程。之后加入创建的ThreadGroup,但是ThreadGroup好像没有join()方法。啊 :)
      • 您总是可以在该 ThreadGroup 的测试中旋转 activeCount() > 0(但这会捕获子线程组吗?)。
      【解决方案3】:

      @Eyal 概述的基本技术是 ConcurrentUnit 的用途。一般用法是:

      1. 产生一些线程
      2. 让主线程等待或休眠
      3. 在工作线程中执行断言(通过 ConcurrentUnit 报告回主线程)
      4. 所有断言完成后,从其中一个工作线程中恢复主线程

      有关详细信息,请参阅 ConcurrentUnit 页面。

      【讨论】:

        【解决方案4】:

        也许用 ExecutorService 将您的线程分组,然后使用 shutdownawaitTermination 方法?

        条件是使用 Runnable 或 Future,而不是线程本身。

        【讨论】:

        • JUnit 不等待 ExecutorService 完成。
        【解决方案5】:
                while (s.isRunning()) {
                    try {
                        Thread.sleep(SLEEP_TIME);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
        

        您可以尝试阻止 JUnit 线程并让它等待您的线程完成。 Thread.sleep() 是必需的,这样您的线程就不会占用 CPU。在这个例子中 s 将是你的线程,你需要有一个 isRunning() 方法,这样你就可以每隔 SLEEP_TIME 毫秒检查线程是否仍在运行。我知道这不是最好的解决方案,但是如果您只有 2 个线程并且您不希望 JUnit 杀死您的线程,那么这是可行的。 while 循环导致此 JUnit 线程保持活动状态并等待您的其他线程完成。

        【讨论】:

          【解决方案6】:

          在java中,尝试使用CountDownLatch让主线程等到子线程完成。

              @Test
              public void myTest() {
                  CountDownLatch countDownLatch = new CountDownLatch(1);
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          //write your logic here
                          countDownLatch.countDown();
                      }
                  }).start();
                  //wait until the child thread completed.
                  countDownLatch.await();
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-06-07
            • 2019-01-27
            • 1970-01-01
            • 2018-12-12
            相关资源
            最近更新 更多