单元测试由已处理的线程检索的结果

时间:2010-06-22 12:15:42

标签: c# multithreading unit-testing mocking

我有一个单元测试,我在其中模拟(使用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
    }

2 个答案:

答案 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