即使调用方法,Moq验证方法也会失败

时间:2011-05-31 07:57:15

标签: c# .net unit-testing mocking moq

我在使用Moq时遇到了一些麻烦。单元测试后面会抛出异常,即使将调用相应的方法。

[TestMethod]
public void CreateFinishTest() {
    // mock methods 
    factoryMock.Setup(f => f.LoadPlan("TestPlanDoNotUse")).Returns(testPlan).Verifiable();
    factoryMock.Setup(f => f.CreateFinish(It.IsAny<CreateFinishMessage>(), It.IsAny<string>())).Returns(testFinish.Id).Verifiable();

    try {
        var cfm = new CreateFinishMessage() {
            ClientId = 11,
            MessageId = 23456,
            CustomerId = 6,
            FinishName = "MyFinish",
            PlanId = "TestPlanDoNotUse"
        };
        var cmd = sysCfg.Executor.CreateFinish(cfm); // calls LoadPlan with cfm.PlanId and CreateFinish with cfm and cfm.PlanId
        sysCfg.Executor.Execute(cmd);

        factoryMock.Verify(f => f.LoadPlan("TestPlanDoNotUse"), Times.Exactly(1));
        factoryMock.Verify(f => f.CreateFinish(It.IsAny<CreateFinishMessage>(), It.IsAny<string>()), Times.Exactly(1));
    } catch (Exception exc) {
        Assert.Fail(exc.Message);
    }
}

发生此错误:

Expected invocation on the mock exactly 1 times, but was 0 times: f => f.LoadPlan("TestPlanDoNotUse")

Configured setups:
f => f.LoadPlan("TestPlanDoNotUse"), Times.Once

Performed invocations:
IFactory.LoadPlan("TestPlanDoNotUse")
Factory.CreateFinish(IndiValue.LiveMarket.IndiCore.Communication.MessagingFormat.CreateFinishMessage, "MyFinish")

我尝试了几种不同的验证呼叫但它不起作用。并且发生的错误似乎很混乱,它表示永远不会调用LoadPlan("TestPlanDoNotUse"),但它列出了@Performed invocations。

问题解决了:

我想我发现了问题,这不是Moq的问题。在sysCfg.Executor.CreateFinish(cfm)中创建并启动了一个新线程。此线程未完成,因此factoryMock.Verify(...)失败。

我使用了AutoResetEvents:

// create AutoResetEvent triggers
AutoResetEvent m_testTrigger1 = new AutoResetEvent(false);

// mock methods     
factoryMock.Setup(f => f.LoadPlan(It.IsAny<string>())).Returns(testPlan).Callback(() => m_testTrigger1.Set());

// do something

// wait for triggers
bool didReturn1 = m_testTrigger1.WaitOne(timeOut);

3 个答案:

答案 0 :(得分:6)

您通常不会在设置中使用Verifiable()和Verify(expr,times)方法。如果删除.Verifiable()调用,它是否有效?

答案 1 :(得分:6)

在Verifiable未被调用时,期望中的参数与生产代码使用的参数匹配非常重要。

关于 Thread.Sleep 的使用,尽可能避免使用它,因为它只会减慢测试速度以满足您最慢的机器。我通常会将WaitHandles引入我的测试中,以确保测试的运行速度与代码一样快。

使用WaitHandles和事件peek here on a small utility

答案 2 :(得分:3)

我想这也是我的答案,但我相信这是一个比之前提到的更简单的解决方案。

我实现了一个WaitFor函数,该函数利用lambda回调来评估条件:

public static void WaitFor(Func<bool> action, long timeoutMillis = 10000) { Stopwatch elapsed = Stopwatch.StartNew(); elapsed.Start(); // ReSharper disable once LoopVariableIsNeverChangedInsideLoop while (!action()) { if (elapsed.ElapsedMilliseconds > timeoutMillis) { throw new TimeoutException("Timed out waiting for condition to become true after " + elapsed.ElapsedMilliseconds + " ms"); } Thread.Sleep(0); } }

,测试代码如下所示:

    [Test]
    public void ShouldNackUnparsableInboundMessage()
    {   
        var nackCalled = false;
        _mock.Setup(m => m.BasicNack(999, false, false)).Callback(() =>
        {
            nackCalled = true;
        });

        ... do whatever which invokes the call on another thread.

        WaitFor(() => nackCalled);
        // Test will fail with timeout if BasicNack is never called.
    }