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

时间:2020-03-06 14:30:45  来源:igfitidea点击:

有没有人对以一致的方式对多线程应用程序进行单元测试的建议?我已经完成了一个应用程序,其中我们的模拟"工作线程"具有thread.sleep,其时间由公共成员变量指定。我们将使用它来设置特定线程完成其工作所花费的时间,然后进行声明。有更好的方法可以做到这一点吗? .Net可以处理此问题的任何良好模拟框架吗?

解决方案

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

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

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

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

像GUI单元测试一样,这对于自动测试来说有点像滑铁卢。真正的线程根据定义是..不可预测的..它们将以无法预先确定的方式进行干预。因此,即使不是不可能,编写真实的测试也很困难。

但是,我们有一些与此相关的公司...我建议搜索testdrivendevelopment yahoo组的档案。我记得附近有一些帖子。.这是较新的帖子之一。
(如果某人足够友善,可以直言不讳。)那太好了。我太困了。

如果我们必须测试后台线程是否可以执行某些操作,那么我觉得很方便的一种简单方法是拥有一个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));

这样,我们不必长时间休眠主测试线程就可以让后台线程有时间完成。如果背景没有在合理的时间内结束,则测试将失败。

我的建议是不要出于以下几个原因而依靠单元测试来检测并发问题:

  • 缺乏可重复性:测试只会偶尔失败一次,并且对查明问题并没有真正的帮助。
  • 错误的构建失败会惹恼团队中的每个人-因为总是会错误地怀疑最后一次提交是构建失败的原因。
  • 遇到死锁时,可能会冻结构建,直到遇到执行超时为止,这可能会大大减慢构建速度。
  • 构建环境可能是单个CPU环境(认为构建在VM中运行),无论设置多少睡眠时间,都不会发生并发问题。
  • 它以某种方式挫败了具有简单,隔离的验证代码单元的想法。

在多处理器机器上测试多线程代码很重要。双核计算机可能不足。我已经看到死锁发生在4处理器的计算机上,而死锁没有发生在双核单处理器上。然后,我们需要基于客户端程序创建压力测试,该客户端程序会产生许多线程并针对目标应用程序发出多个请求。如果客户端计算机也是多处理器的,则也有帮助,因此目标应用程序上的负载更多。

第一步是要认识到,通常需要进行单元测试的许多代码都与线程正交。这意味着我们应该尝试将执行工作的代码与执行线程的代码分开。完成之后,我们可以使用正常的单元测试实践轻松地测试完成工作的代码。但是,当然,我们知道这一点。

然后,问题是测试问题的线程方面之一,但是至少现在我们已经知道该线程与完成工作的代码接口,并希望在那里有一个可以模拟的接口。既然我们已经有了线程模拟所调用的代码的模拟,我发现最好的事情就是向模拟添加一些事件(这可能意味着我们需要手动滚动模拟)。然后,这些事件将用于允许测试与被测线程代码同步并阻止该线程代码。

例如,假设我们有一个非常简单的东西,一个处理工作项的多线程队列。我们会嘲笑工作项。该接口可能包含线程调用以完成工作的'Process()'方法。我们将在其中放置两个事件。模拟程序在调用Process()时设置的一个,而模拟程序在设置第一个事件后等待的设置。现在,在测试中,我们可以启动队列,发布模拟工作项,然后等待工作项的"我正在处理"事件。如果我们要测试的只是调用该进程,则可以设置另一个事件并让线程继续。如果要测试更复杂的内容(例如队列如何处理多个调度),则可以在释放线程之前执行其他操作(例如发布并等待其他工作项)。由于可以在测试中等待超时,因此可以确保(例如)仅并行处理两个工作项,依此类推,等等。关键是要使用线程代码阻塞的事件使测试具有确定性测试可以控制它们何时运行。

我相信情况会更复杂,但这是我用来测试线程代码的基本方法,并且效果很好。如果我们模拟出正确的位并放入同步点,则可以对多线程代码进行令人惊讶的控制。

尽管这是关于C ++代码库的信息,但这里有一些更多信息:http://www.lenholgate.com/blog/2004/05/practical-testing.html

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

我将多线程TC库从Java移植到了.NET,并将其命名为TickingTest。它使我们可以从单元测试方法中启动多个线程并进行协调。它没有原始功能,但我发现它很有用。它最缺少的是监视测试期间启动的线程的能力。

我遇到了一个名为Microsoft Chess的研究产品。它专门为多线程应用程序的非确定性测试而设计。到目前为止,缺点是它已集成到VS中。