我们正在使用TDD开发WPF应用程序。由于我们已经在这个解决方案上工作了近两年,我们已经编写了大量的测试(现在几乎有2000个单元测试)。
有些类需要实现多线程和异步功能。例如,可以发送和接收消息并解析消息的通信组件。始终使用RhinoMocks模拟依赖项。
我们针对这些类的测试方法看起来非常相似,如下所示:
[TestMethod]
public void Method_Description_ExpectedResult(){
// Arrange
var myStub = MockRepository.GenerateStub<IMyStub>();
var target = new MyAsynchronousClass(myStub);
// Act
var target.Send("Foo");
Thread.Sleep(200);
//Assert
myStub.AssertWasCalled(x => x.Bar("Foo"));
}
如您所见,由于Thread.Sleep(),此测试至少运行200 ms。我们使用主动轮询方法(s.th.)优化了替换AssertWasCalled的测试。像这样:
public static bool True(Func<bool> condition, int times, int waitTime)
{
for (var i = 0; i < times; i++)
{
if (condition())
return true;
Thread.Sleep(waitTime);
}
return condition();
}
我们现在可以通过将AssertWasCalled更改为:
来使用此WaitFor.True(...)方法var fooTriggered = false;
myStub.Stub(x => x.Bar("Foo")).Do((Action)(() => fooTriggered = true)));
WaitFor.True(() => fooTriggered, 20, 20);
Assert.IsTrue(fooTriggered);
如果条件匹配,此构造将提前终止,但无论如何 - 这对我们来说需要太长时间。运行我们所有的2000次测试需要大约5分钟(构建和运行它们)。
我们如何才能优化这样的代码?
答案 0 :(得分:2)
您可以使用显示器。我正在做这个,所以请原谅我,如果它没有完全编译,但它看起来像:
[TestMethod]
public void Method_Description_ExpectedResult(){
// Arrange
var waitingRoom = new object();
var myStub = MockRepository.GenerateStub<IMyStub>();
myStub.Setup(x => x.Bar("Foo")).Callback(x =>
{
Monitor.Enter(waitingRoom);
Monitor.Pulse(waitingRoom);
Monitor.Exit(waitingRoom);
}
var target = new MyAsynchronousClass(myStub);
// Act
Monitor.Enter(waitingRoom);
target.Send("Foo");
Monitor.Wait(waitingRoom);
Monitor.Exit(waitingRoom);
//Assert
myStub.AssertWasCalled(x => x.Bar("Foo"));
}
在Monitor中编写的代码无法运行,直到它是免费的。测试将导致代理线程一直等到Monitor.Wait被调用。然后回调可以进入并脉冲监视器。然后测试“唤醒”,一旦回调退出监视器,它就会得到控制并退出,允许你断言。
我唯一没有涉及的是,如果Bar(“Foo”)没有被调用它会挂起,所以你可能也希望有一个定时器脉冲线程。
如果您经常使用它,您可以创建一个为您执行复杂监视位的类。 This is one I wrote处理UI自动化中的异步检查;根据你的工作进行调整可能会对你有所帮助。