单元测试并发软件 - 你做什么?

时间:2010-07-29 13:05:16

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

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

在过去的好时光中,你有一个类型,你打电话给它,你检查了它返回的内容和/或它所调用的其他内容。

现在,您调用一个方法,并且实际工作计划在下一个可用线程上运行;你不知道什么时候它会真正启动并调用其他东西 - 更重要的是,其他东西也可能是并发的。

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

您遇到哪些资源帮助过您?


修改

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


6 个答案:

答案 0 :(得分:5)

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

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

Jinx适用于Linux和Windows,以及本机代码和托管代码。它是语言和应用程序平台无关的,可以使用您现有的所有工具。

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

答案 1 :(得分:4)

我建议您选择Growing Object Oriented Software by Freeman and Pryce的副本。最后几章非常具有启发性并涉及这一特定主题。它还介绍了一些有助于确定讨论符号的术语。

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

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

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

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

答案 2 :(得分:3)

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

我知道早期alpha / beta阶段有两个这样的工具:

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

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

对于集成测试,我只是明确调用后台任务,然后检查后面的期望。

在Cucumber中它看起来像这样:

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

答案 5 :(得分:0)

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

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

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