【问题标题】:Unit testing concurrent software - what do you do?单元测试并发软件 - 你做什么?
【发布时间】:2010-07-29 13:05:16
【问题描述】:

随着软件变得越来越并发,您如何处理类型的核​​心行为与您的单元测试(不是并行行为,只是核心行为)?

在过去的美好时光里,你有一个类型,你调用它,然后检查它返回的内容和/或它调用的其他内容。

现在,您调用一个方法,实际工作被安排在下一个可用线程上运行;你不知道它什么时候真正开始并调用其他的东西——更重要的是,那些其他的东西也可能是并发的。

你如何处理这个问题?您是否抽象/注入并发调度程序(例如抽象任务并行库并在单元测试中提供假/模拟)?

你遇到过哪些对你有帮助的资源?


编辑

我已编辑问题以强调测试类型的正常行为(忽略任何用于利用多核的并行机制,例如 TPL)


【问题讨论】:

    标签: unit-testing concurrency parallel-extensions parallel-processing


    【解决方案1】:

    免责声明:我在西雅图的一家小型初创公司 Corensic 工作。我们有一个名为 Jinx 的工具,旨在检测代码中的并发错误。在我们处于 Beta 阶段时,它现在是免费的,所以您可能想查看一下。 (http://www.corensic.com/)

    简而言之,Jinx 是一个非常薄的虚拟机管理程序,当它被激活时,它会滑入处理器和操作系统之间。 Jinx 然后智能地获取执行切片并运行各种线程时序的模拟以查找错误。当我们发现会导致错误发生的特定线程计时,我们会在您的机器上使该计时“成为现实”(例如,如果您使用的是 Visual Studio,调试器将在该点停止)。然后,我们指出代码中导致错误的区域。 Jinx 没有误报。当它检测到一个错误时,它肯定是一个错误。

    Jinx 可以在 Linux 和 Windows 上运行,并且可以在本机代码和托管代码中使用。它与语言和应用程序平台无关,可以与您现有的所有工具一起使用。

    如果您检查了它,请向我们发送反馈,说明哪些有效,哪些无效。我们一直在一些大型开源项目上运行 Jinx,并且已经看到 Jinx 发现错误的速度比简单的压力测试代码快 50-100 倍。

    【讨论】:

    • 这看起来很有趣。并发问题可能需要几天或几周甚至几个月的时间才能发现,而像这样的分析检测它们的机制可能非常有价值。
    【解决方案2】:

    我建议选择一份Growing Object Oriented Software by Freeman and Pryce。最后几章非常有启发性,并处理了这个特定的主题。它还介绍了一些有助于确定符号以供讨论的术语。

    总结.... 他们的核心思想是拆分功能和并发/同步方面

    • 首先在单个同步线程中像普通对象一样测试功能部分
    • 一旦您确定了功能部分。您可以转到并发方面。为此,您必须考虑并为您的对象提出“observable invariants w.r.t. concurrency”,例如计数应该等于调用该方法的次数。一旦你确定了不变量,你就可以编写运行多个线程 et.all 的压力测试来尝试打破你的不变量。压力测试会断言您的不变量。
    • 最后作为附加防御,运行工具或静态分析来查找错误。

    对于被动对象,即在不同线程上从客户端调用的代码:您的测试需要通过启动自己的线程来模拟客户端。然后,您需要在通知侦听或采样/轮询方法之间进行选择,以将您的测试与 SUT 同步。

    • 您可以阻止,直到收到预期的通知
    • 在合理的超时时间内轮询某些可观察到的副作用。

    【讨论】:

      【解决方案3】:

      竞争条件和死锁的单元测试领域相对较新,缺乏好的工具。

      我知道两个处于早期 alpha/beta 阶段的此类工具:

      另一种选择是尝试编写一个“压力测试”,这会导致死锁/竞争条件浮出水面,创建多个实例/线程并并排运行它们。这种方法的缺点是,如果测试失败,将很难重现它。我建议在测试和生产代码中都使用日志,以便您能够了解发生了什么。

      【讨论】:

      • 感谢链接 +1 - 我相信它们会对阅读此问题的任何人派上用场。我已经编辑了我的问题,因为我真的想问更多关于类型的正常行为而不是并发行为。
      【解决方案4】:

      我发现一种有用的技术是在英特尔 Parallel Inspector 等检测竞争条件的工具中运行测试。测试运行速度比正常慢得多,因为必须检查对时间的依赖性,但是单次运行可以发现错误,否则需要数百万次重复的普通运行。

      我发现这在通过多核转换现有系统以实现细粒度并行时非常有用。

      【讨论】:

        【解决方案5】:

        单元测试真的不应该测试并发/异步行为,你应该在那里使用模拟并验证模拟接收到预期的输入。

        对于集成测试,我只是显式调用后台任务,然后检查预期。

        在 Cucumber 中是这样的:

        When I press "Register"
        And the email sending script is run
        Then I should have an email
        

        【讨论】:

        • 好点 - 我的问题并不清楚我指的是测试核心行为而不是并行行为。我已经编辑了问题。
        【解决方案6】:

        鉴于您的 TPL 将有自己的单独单元测试,您无需验证。

        鉴于我为每个模块编写了两个测试:
        1) 使用一些环境变量或#define 来打开 TPL 的单线程单元测试,以便我可以测试我的模块的功能正确性。
        2) 以线程可部署模式运行模块的压力测试。该测试试图找出并发问题,并且应该使用大量随机数据。

        第二个测试通常包括许多模块,因此可能更多的是集成/系统测试。

        【讨论】:

          猜你喜欢
          • 2010-10-05
          • 2010-10-13
          • 2010-12-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-06
          • 1970-01-01
          相关资源
          最近更新 更多