12

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

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

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

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

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


编辑

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


4

6 回答 6

5

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

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

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

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

于 2010-07-29T18:06:17.200 回答
4

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

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

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

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

  • 您可以阻止直到收到预期的通知
  • 使用合理的超时轮询某些可观察到的副作用。
于 2010-07-31T10:02:41.703 回答
3

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

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

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

于 2010-07-29T14:24:11.750 回答
1

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

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

于 2010-07-29T13:59:02.427 回答
0

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

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

在 Cucumber 中,它看起来像这样:

When I press "Register"
And the email sending script is run
Then I should have an email
于 2010-07-29T13:25:56.937 回答
0

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

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

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

于 2010-07-29T23:36:50.147 回答