Метод проверки Moq завершается ошибкой, даже если метод будет - PullRequest
6 голосов
/ 31 мая 2011

У меня проблемы с использованием 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")

Я пробовал несколько разных Verify-Calls, но это не сработает. И ошибка, которая возникает, кажется довольно запутанной, она говорит, что LoadPlan("TestPlanDoNotUse") никогда не вызывается, но она указана в списке «Выполненные вызовы».

Проблема решена:

Я думаю, что нашел проблему, это была не проблема 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 ]

6 голосов
/ 01 июня 2011

Если Verifiable не вызывается, важно, чтобы аргументы в вашем ожидании соответствовали аргументам, которые используются рабочим кодом.

Относительно использования Thread.Sleep ,избегайте его везде, где это возможно, поскольку это только замедлит тесты, чтобы соответствовать вашей самой медленной машине.Обычно я включаю WaitHandles в свои тесты, чтобы тесты выполнялись так же быстро, как и код.

Взгляните на небольшую утилиту , которая использует WaitHandles с событиями.

6 голосов
/ 31 мая 2011

Обычно вы не используете Verifiable () в ваших установках в сочетании с методами Verify (expr, times). Работает ли это, если вы удалите вызовы .Verifiable ()?

3 голосов
/ 25 сентября 2015

Полагаю, это ответ "я тоже", но я считаю, что это более простое решение, чем многие из упомянутых ранее.

Я реализовал функцию WaitFor, которая использует лямбда-обратный вызов для оценки условия:

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.
    }
...