0

我们的项目确实有一堆单元测试,它们也在 TFS 2012 构建服务器上的 CheckIn 期间运行。虽然它们在本地的行为与预期的非常相似,但偶尔在 TFS 上使用计时器的单元测试会发生一些奇怪的行为。最后,我们编写了一个类和一个测试来隔离这种行为。

班上:

public class TestTimerOnServer    
{    
    Timer _MyTimer = new Timer(100);    
    int _CountsToDo = 100;    
    public int TimerElapsedCounter = 0;

    public TestTimerOnServer()    
    {    
        _MyTimer.Elapsed += _MyTimer_Elapsed;    
    }

    public void StartCounting(int argNumberOfCounts, int argIntervalInMs)    
    {    
        _MyTimer.Stop();    
        _MyTimer.Interval = argIntervalInMs;    
        TimerElapsedCounter = 0;    
        _CountsToDo = argNumberOfCounts;    
        _MyTimer.Start();    
    }

    void _MyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)    
    {    
        if (_CountsToDo > TimerElapsedCounter)    
            TimerElapsedCounter++;    
        else    
            _MyTimer.Stop();    
    }    
}

针对它运行的测试:

private void TestTimerServerBehaviour()   
{   
   TestTimerOnServer ttos = new TestTimerOnServer();   
   ttos.StartCounting(10, 100); // should lead 1000 ms

   // we wait 1100 MS   
   Thread.Sleep(1100);

   Assert.AreEqual(ttos.TimerElapsedCounter, 10,"We should have 10 
   samples, but only " + ttos.TimerElapsedCounter + " have been generated.!"); 

   ttos.StartCounting(50, 100); // should lead 5000 ms

   // we wait 6000 MS   
   Thread.Sleep(6000);

   Assert.AreEqual(ttos.TimerElapsedCounter, 50, "We should have 50 
   samples, but only " + ttos.TimerElapsedCounter + " have been generated.!");   
}

大多数情况下,该测试通过,偶尔它在 TFS 上失败 - 仅触发事件两次而不是 50 次。所以问题是我们在这里遇到了一般的单元测试设计缺陷,还是在 TFS 构建服务运行测试时发现问题?

编辑: TFS 在虚拟机上运行——这可能是我们问题的根源吗?

EDIT2 - 最终调查结果 问题显然是Testing-Context和VMware的结合。我使用@allen 发布的代码编写了一个小应用程序,一个使用相同代码的测试和另一个导致cpu 负载的测试:

void CPULoader()
{
    var action = new Action(() => { while (true);});

    Parallel.Invoke(action, action, action, action, action, action, action, action); 
}

相当原始但有效。

在我运行应用程序的硬件机器上,CPU-Loader 会导致计时器事件稍晚发生,例如 1100 毫秒而不是 1000 毫秒。到目前为止还不错,Win7 毕竟不是实时系统。

在 VMware 上,某些组合会导致长达 5000 毫秒的延迟,而应该有一秒钟...... - 当然更糟,但仍然会发生事件。

真正的乐趣从运行测试开始(为此我将代码打包成一个无限循环),然后启动 CPU-Loader。整个系统非常慢,直到我停止加载 CPU 并且在测试之前没有发生该事件

希望这可以给某人一些提示 - 我们通过使用 Timer 抽象并在单元测试时模拟计时行为来修复我们的测试。作为副作用,测试现在运行得更快:)

4

2 回答 2

1

我的怀疑是在虚拟机上。使用 VM,您可能会遇到与时间同步相关的问题,这些问题可能会导致与时间相关的功能遇到问题。验证这一点的一种方法是编写一段简单的代码并在同一个 VM 上运行它。

Stopwatch watch = new Stopwatch();
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 30*60 * 1000;
timer.Elapsed += timer_Elapsed;
watch.Start();
timer.Start();

static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    watch.Stop();
    Console.WriteLine("Stopwatch value - {0}", watch.Elapsed);
    watch.Restart();
}

另一个示例也在 stackoverflow 上System.Diagnostics.Stopwatch 在 Elapsed... 属性中返回负数

可能导致这种情况的一些因素是 a) 在 VM 上禁用时间同步服务 b) 主机上缺乏资源,例如。虚拟机太多

我的建议是在主机上设置一个新的虚拟机,并为该虚拟机提供更多资源

于 2013-02-27T18:57:38.197 回答
1

系统调用sleep和亲属经常与信号处理程序交互并提前中止他们的睡眠。对于 Unix (3) 函数,这种现象得到了最好的记录sleep,但我怀疑它也适用于您的 API。您应该通过在睡眠后检查当前时间来防止短睡眠。

通常,您希望尽可能将计时器单元测试与系统时钟分离。在处理基于队列的计时器和可以从外部调用的心跳函数时,我有非常好的经验,但是您应该始终能够模拟对函数的调用sleep和时间获取函数,然后在不调用实际函数的情况下进行测试。

于 2013-02-26T10:48:26.183 回答