我们的项目确实有一堆单元测试,它们也在 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 抽象并在单元测试时模拟计时行为来修复我们的测试。作为副作用,测试现在运行得更快:)