【问题标题】:Unit testing a multithreaded application?对多线程应用程序进行单元测试?
【发布时间】:2010-09-11 19:39:15
【问题描述】:

对于以一致的方式对多线程应用程序进行单元测试,有人有什么建议吗?我已经完成了一个应用程序,其中我们的模拟“工作线程”有一个 thread.sleep,其时间由公共成员变量指定。我们将使用它,以便我们可以设置特定线程完成其工作所需的时间,然后我们可以进行断言。有更好的方法来做到这一点吗?有什么好的 .Net 模拟框架可以处理这个问题?

【问题讨论】:

标签: .net multithreading unit-testing


【解决方案1】:

我遇到了一个名为Microsoft Chess 的研究产品。它专为多线程应用程序的非确定性测试而设计。到目前为止的缺点是它被集成到 VS 中。

【讨论】:

【解决方案2】:

第一步是要意识到,您需要进行单元测试的大部分代码通常与线程正交。这意味着您应该尝试将执行工作的代码与执行线程的代码分开。完成后,您可以使用正常的单元测试实践轻松测试完成工作的代码。但是,你当然知道。

问题在于测试问题的线程方面,但至少现在您有一点,该线程与执行工作的代码接口,并且希望您有一个可以模拟的接口。现在您已经有了线程代码调用的代码的模拟,我发现最好的办法是向模拟添加一些事件(这可能意味着您需要手动滚动模拟)。然后这些事件将用于允许测试与被测线程代码同步并阻塞。

例如,假设我们有一个非常简单的东西,一个处理工作项的多线程队列。你会模拟工作项。该接口可能包含线程调用以完成工作的“Process()”方法。你会在那里放两个事件。一个是在调用 Process() 时模拟设置的,另一个是模拟在设置第一个事件后等待的。现在,在您的测试中,您可以启动队列,发布模拟工作项,然后等待工作项的“我正在处理”事件。如果您正在测试的只是该进程被调用,那么您可以设置另一个事件并让线程继续。如果您正在测试更复杂的东西,例如队列如何处理多个调度或其他东西,那么您可能会在释放线程之前做其他事情(例如发布并等待其他工作项)。由于您可以在测试中等待超时,因此您可以确保(例如)只有两个工作项被并行处理,等等。关键是您使用线程代码阻塞的事件使测试具有确定性以便测试可以控制它们何时运行。

我确定您的情况更复杂,但这是我用来测试线程代码的基本方法,并且效果很好。如果您模拟出正确的位并放入同步点,您可以对多线程代码进行惊人的控制。

这里有一些关于这类事情的更多信息,虽然它是在谈论 C++ 代码库:http://www.lenholgate.com/blog/2004/05/practical-testing.html

【讨论】:

    【解决方案3】:

    我不认为单元测试是查找线程错误的有效方法,但它们可以是展示已知线程错误、隔离它并测试你的修复的好方法。我还使用它们来测试我的应用程序中某些协调类的基本功能,例如阻塞队列。

    我将 multithreadedTC 库从 Java 移植到 .NET,并将其命名为 TickingTest。它允许您从单元测试方法启动多个线程并协调它们。它没有原版的所有功能,但我发现它很有用。它最缺的就是监控测试期间启动的线程的能力。

    【讨论】:

      【解决方案4】:

      如果您必须测试后台线程是否执行某项操作,我觉得很方便的一个简单技术是拥有一个 WaitUntilTrue 方法,它看起来像这样:

      bool WaitUntilTrue(Func<bool> func,
                    int timeoutInMillis,
                    int timeBetweenChecksMillis)
      {
          Stopwatch stopwatch = Stopwatch.StartNew();
      
          while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
          {
              if (func())
                  return true;
              Thread.Sleep(timeBetweenChecksMillis);
          }   
          return false;
      }
      

      这样使用:

      volatile bool backgroundThreadHasFinished = false;
      //run your multithreaded test and make sure the thread sets the above variable.
      
      Assert.IsTrue(WaitUntilTrue(x => backgroundThreadHasFinished, 1000, 10));
      

      这样您就不必让主测试线程长时间休眠以给后台线程时间来完成。如果后台没有在合理的时间内完成,则测试失败。

      【讨论】:

      • 恕我直言,最好使用事件来同步和等待,而不是像那样旋转;但我原则上同意。
      • @Len:这种方法的优点是通常很容易编写一个小函数来确定被测代码是否已完成,但您不想修改代码以发出特殊的信号量仅用于测试。
      • 我的大部分代码已经具有用于生产用途的监控钩子,并且这些钩子往往可以很好地提供测试可以插入他们可以等待的模拟的点。如果您不那样设计,那么我猜旋转比不测试要好;)
      • 查看 MSDN 博客中的这篇文章:使用 .NET 进行并行编程 SpinWait.SpinUntil for unit testing 我投票支持 SpinWait.SpinUntil
      【解决方案5】:

      在多处理器机器上测试多线程代码很重要。双核机器可能还不够。我已经看到死锁发生在 4 处理器机器上,而双核单处理器不会发生死锁。然后,您需要基于生成许多线程并对目标应用程序发出多个请求的客户端程序创建压力测试。如果客户端机器也是多处理器的,这会有所帮助,因此目标应用程序的负载会更大。

      【讨论】:

        【解决方案6】:

        我的建议是不要依赖单元测试来检测并发问题,原因如下:

        • 缺乏可重复性:测试只会偶尔失败一次,并且对查明问题没有真正的帮助。
        • 不稳定的构建失败会惹恼团队中的每个人 - 因为最后一次提交总是被错误地怀疑是构建失败的原因。
        • 遇到死锁可能会冻结构建,直到遇到执行超时,这会显着减慢构建速度。
        • 构建环境很可能是一个单 CPU 环境(认为构建在 VM 中运行),其中可能永远不会发生并发问题 - 无论设置了多少休眠时间。
        • 它以某种方式挫败了拥有简单、孤立的单元验证代码的想法。

        【讨论】:

        • 次要评论:有时可以编写一个单元测试来证明竞争条件/死锁。如果您可以在您(或所有者)修复代码之前做到这一点,那么您已经帮助解释了问题并确保修复是足够的。否则同意
        • -1 - 我不同意,但我已经成功地使用单元测试来测试并发代码一段时间了。诀窍在于您从测试中控制被测代码的方式 - 请参阅我对原始问题的回复。恕我直言,您可以通过尝试尽可能多地使用单元测试来测试代码所面临的并发问题。这很难,它会让你对被测代码和一般的并发进行认真思考;其中,恕我直言是一件好事。
        • 虽然这个建议可能是有效的,但它是“不该做什么”的模板,它回避了应该做什么。依赖确定性单元测试(不应具有线程计时或多组件依赖)和彻底的多组件回归测试的组合。 Len Holgate 的回答为单元测试并发问题提供了一个很好的模板。
        【解决方案7】:

        与 GUI 单元测试一样,这有点像自动化测试的滑铁卢。根据定义,真正的线程是......不可预测的......它们会以无法预先确定的方式进行干扰。因此编写 true 测试即使不是不可能也很困难。

        但是你有一些公司在这...我建议搜索 testdrivendevelopment yahoo 组的档案。我记得附近有一些帖子..Here's one of the newer ones. (如果有人愿意扫射和解释......那就太好了。我太困了......需要 LO SO)

        【讨论】:

          【解决方案8】:

          不完全是单元测试,但您可以编写一些测试代码,重复调用将在不同线程上执行的代码。尝试通过定期或最终一致性检查在线程之间创建最大交错。当然,这种方法具有不可重现的缺点,因此您需要使用大量日志记录来找出问题所在。这种方法最好与每个线程单独任务的单元测试结合使用。

          【讨论】:

            【解决方案9】:

            TypeMock(商业)有一个单元测试框架,可以自动尝试在多线程应用程序中查找死锁,我认为可以设置为运行具有可预测上下文切换的线程。

            我本周在一个节目中看到了一个演示 - 显然是在 Alpha(称为 Racer)中

            http://www.typemock.com/Typemock_software_development_tools.html

            【讨论】:

            • 这是我对 TypeMock 的期望,例如让做错事变得容易,因为有时做错事比什么都不做要好。
            猜你喜欢
            • 2019-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-03-21
            • 2011-03-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多