Moq验证方法失败,即使方法将被称为

Moq验证方法失败,即使方法将被称为

问题描述:

我有一些麻烦使用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。

问题解决了:

我想我找到了问题,它不是一个起订量的问题。在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); 

在未被调用的Verifiable上,重要的是您期望中的参数与生产代码正在使用的参数相匹配。

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

取一个使用WaitHandles事件的peek here on a small utility

+0

我使用了AutoResetEvents: – Robar 2011-06-27 06:29:58

你通常不会与验证(表达式,时间)方法一起选择在你的设置使用可验证()。如果您删除.Verifiable()调用,它会起作用吗?

+0

如果删除了.Verifiable()调用我得到以下错误:'预期调用模拟正好1次,但是0次:f => f.CreateFinish(It.IsAny (),It.IsAny ()) 配置的setu PS: F => f.CreateFinish(It.IsAny (),It.IsAny ()),Times.Never 演出调用: IFactory.LoadPlan( “TestPlanDoNotUse”)' – Robar 2011-05-31 08:44:11

+0

这看起来有点更好,如果这确实是整个错误信息。现在看起来像CreateFinish不会被调用。它是否说过为CreateFinish执行的调用? – TheFogger 2011-05-31 09:00:26

+0

不,它没有说关于CreateFinish的invoaction的任何事情,但我调试了它,我想我找到了问题:看到我的答案。感谢您的帮助 – Robar 2011-05-31 09:34:26

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

我实现了一个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. 
    }