单元测试多线程应用程序?

时间:2008-09-21 18:43:20

标签: .net multithreading unit-testing

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

9 个答案:

答案 0 :(得分:27)

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

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

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

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

以下是关于此类事情的更多信息,尽管它讨论的是C ++代码库:http://www.lenholgate.com/blog/2004/05/practical-testing.html

答案 1 :(得分:22)

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

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

答案 2 :(得分:10)

如果你必须测试一个后台线程做了什么,我发现一个简单的技术是使用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));

这样您就不必长时间睡眠主测试线程,以便让后台线程有时间完成。如果背景未在合理的时间内完成,则测试失败。

答案 3 :(得分:7)

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

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

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

答案 4 :(得分:4)

我遇到了一种名为Microsoft Chess的研究产品。它专为多线程应用程序的非确定性测试而设计。到目前为止,下行是它被整合到VS.

答案 5 :(得分:2)

在多处理器计算机上测试多线程代码非常重要。双核机器可能还不够。我已经看到死机发生在4处理器机器上,而双核单处理器上却没有。然后,您需要基于客户端程序创建压力测试,该客户端程序会生成许多线程并对目标应用程序发出多个请求。如果客户端计算机也是多处理器,那么它会对目标应用程序产生更多负载。

答案 6 :(得分:2)

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

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

与GUI单元测试一样,这是一个用于自动测试的滑铁卢。根据定义,真正的线程是不可预测的......它们会干扰无法预先确定的方式。因此,编写真实测试很困难,如果不是不可能的话。

但是你有一些公司...我建议搜索 testdrivendevelopment 雅虎集团的档案。我记得附近的一些帖子.. Here's one of the newer ones. (如果有人愿意接受和解释那就太好了。我太困了......需要这样做)