【发布时间】:2010-09-11 19:39:15
【问题描述】:
对于以一致的方式对多线程应用程序进行单元测试,有人有什么建议吗?我已经完成了一个应用程序,其中我们的模拟“工作线程”有一个 thread.sleep,其时间由公共成员变量指定。我们将使用它,以便我们可以设置特定线程完成其工作所需的时间,然后我们可以进行断言。有更好的方法来做到这一点吗?有什么好的 .Net 模拟框架可以处理这个问题?
【问题讨论】:
标签: .net multithreading unit-testing
对于以一致的方式对多线程应用程序进行单元测试,有人有什么建议吗?我已经完成了一个应用程序,其中我们的模拟“工作线程”有一个 thread.sleep,其时间由公共成员变量指定。我们将使用它,以便我们可以设置特定线程完成其工作所需的时间,然后我们可以进行断言。有更好的方法来做到这一点吗?有什么好的 .Net 模拟框架可以处理这个问题?
【问题讨论】:
标签: .net multithreading unit-testing
我遇到了一个名为Microsoft Chess 的研究产品。它专为多线程应用程序的非确定性测试而设计。到目前为止的缺点是它被集成到 VS 中。
【讨论】:
第一步是要意识到,您需要进行单元测试的大部分代码通常与线程正交。这意味着您应该尝试将执行工作的代码与执行线程的代码分开。完成后,您可以使用正常的单元测试实践轻松测试完成工作的代码。但是,你当然知道。
问题在于测试问题的线程方面,但至少现在您有一点,该线程与执行工作的代码接口,并且希望您有一个可以模拟的接口。现在您已经有了线程代码调用的代码的模拟,我发现最好的办法是向模拟添加一些事件(这可能意味着您需要手动滚动模拟)。然后这些事件将用于允许测试与被测线程代码同步并阻塞。
例如,假设我们有一个非常简单的东西,一个处理工作项的多线程队列。你会模拟工作项。该接口可能包含线程调用以完成工作的“Process()”方法。你会在那里放两个事件。一个是在调用 Process() 时模拟设置的,另一个是模拟在设置第一个事件后等待的。现在,在您的测试中,您可以启动队列,发布模拟工作项,然后等待工作项的“我正在处理”事件。如果您正在测试的只是该进程被调用,那么您可以设置另一个事件并让线程继续。如果您正在测试更复杂的东西,例如队列如何处理多个调度或其他东西,那么您可能会在释放线程之前做其他事情(例如发布并等待其他工作项)。由于您可以在测试中等待超时,因此您可以确保(例如)只有两个工作项被并行处理,等等。关键是您使用线程代码阻塞的事件使测试具有确定性以便测试可以控制它们何时运行。
我确定您的情况更复杂,但这是我用来测试线程代码的基本方法,并且效果很好。如果您模拟出正确的位并放入同步点,您可以对多线程代码进行惊人的控制。
这里有一些关于这类事情的更多信息,虽然它是在谈论 C++ 代码库:http://www.lenholgate.com/blog/2004/05/practical-testing.html
【讨论】:
我不认为单元测试是查找线程错误的有效方法,但它们可以是展示已知线程错误、隔离它并测试你的修复的好方法。我还使用它们来测试我的应用程序中某些协调类的基本功能,例如阻塞队列。
我将 multithreadedTC 库从 Java 移植到 .NET,并将其命名为 TickingTest。它允许您从单元测试方法启动多个线程并协调它们。它没有原版的所有功能,但我发现它很有用。它最缺的就是监控测试期间启动的线程的能力。
【讨论】:
如果您必须测试后台线程是否执行某项操作,我觉得很方便的一个简单技术是拥有一个 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));
这样您就不必让主测试线程长时间休眠以给后台线程时间来完成。如果后台没有在合理的时间内完成,则测试失败。
【讨论】:
在多处理器机器上测试多线程代码很重要。双核机器可能还不够。我已经看到死锁发生在 4 处理器机器上,而双核单处理器不会发生死锁。然后,您需要基于生成许多线程并对目标应用程序发出多个请求的客户端程序创建压力测试。如果客户端机器也是多处理器的,这会有所帮助,因此目标应用程序的负载会更大。
【讨论】:
我的建议是不要依赖单元测试来检测并发问题,原因如下:
【讨论】:
与 GUI 单元测试一样,这有点像自动化测试的滑铁卢。根据定义,真正的线程是......不可预测的......它们会以无法预先确定的方式进行干扰。因此编写 true 测试即使不是不可能也很困难。
但是你有一些公司在这...我建议搜索 testdrivendevelopment yahoo 组的档案。我记得附近有一些帖子..Here's one of the newer ones. (如果有人愿意扫射和解释......那就太好了。我太困了......需要 LO SO)
【讨论】:
不完全是单元测试,但您可以编写一些测试代码,重复调用将在不同线程上执行的代码。尝试通过定期或最终一致性检查在线程之间创建最大交错。当然,这种方法具有不可重现的缺点,因此您需要使用大量日志记录来找出问题所在。这种方法最好与每个线程单独任务的单元测试结合使用。
【讨论】:
TypeMock(商业)有一个单元测试框架,可以自动尝试在多线程应用程序中查找死锁,我认为可以设置为运行具有可预测上下文切换的线程。
我本周在一个节目中看到了一个演示 - 显然是在 Alpha(称为 Racer)中
http://www.typemock.com/Typemock_software_development_tools.html
【讨论】: