我有一个单元测试,我在其中模拟(使用moq)一个对象并让它验证它是否正确执行了一个方法。此方法正在我在SUT(被测系统)中创建的线程中执行。当我想在模拟上执行VerifyAll()时,可能会发生线程仍在运行并且尚未完成但尚未执行该方法 - 未通过测试。
有没有办法在正确的问题上解决这个问题?例如让VerifyAll等待什么?因为现在,测试是不可靠的。
这是测试:
[Test]
public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject()
{
var subject = new Subject();
var rule = new Mock<IBusinessRule>();
rule.Setup(x => x.RunChildren(subject)); //RunChildren will be called in a seperate Thread
InBuffer.TryToExecute(subject, rule.Object);
rule.VerifyAll(); //It could be possible that the Thread is still running and that RunChildren() isn't invoked yet, thus failing the test.
}
public void TryToExecute(Subject subject, IBusinessRule rule){
var thread = new Thread(x =>
{
SetCurrentAsProcessing(subject);
rule.RunChildren(subject) // This is where it executes
RemoveFromProcess(subject);
});
thread.Start(); // Start the Thread
}
答案 0 :(得分:1)
如果你的类实现了一些等待异步操作完成的机制,那么你应该在测试中使用这个。
如果没有,你可以尝试这个“hack”(未经测试 - 我不知道你的模拟框架,我不知道如何让它运行methodCalled.Set()):
[Test]
public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject()
{
ManualResetEvent methodCalled = new ManualResetEvent(false);
var subject = new Subject();
var rule = new Mock<IBusinessRule>();
rule.Setup(x => x.RunChildren(subject)).Do(X=>methodCalled.Set()); //RunChildren will be called in a seperate Thread
InBuffer.TryToExecute(subject, rule.Object);
Assert.IsTrue(methodCalled.WaitOne(1000), "RunChildren was not called within 1000ms");
}
请注意,这种方法并不好,也不安全,所以如果您有其他选择,请避免使用。
答案 1 :(得分:0)
在测试中使用显示器:
Monitor.Enter(lock)
Monitor.Wait(lock, timeout) // Will pause here until pulsed or timed out
Monitor.Exit(lock)
在模拟中使用回调来通知监视器(注意:我的MoQ版本使用Callback
而不是Do
):
rule.Setup(x => x.RunChildren(subject)).Do( X => {
...
Monitor.Enter(lock);
Monitor.Pulse(lock);
Monitor.Exit(lock);
}
这里有一些使用监视器的例子(自动化测试框架的一部分):
http://code.google.com/p/wipflash/source/browse/WiPFlash/Components/AutomationElementWrapper.cs