试图了解MockSequence

时间:2015-09-15 11:50:37

标签: c# moq

我正在编写一个应用程序并测试它的正确行为,我需要验证方法是否按给定顺序调用。

对于我的单元测试,我使用xUnitMoq

现在,为什么我需要测试调用的顺序?

我正在开发一个在不同线程上执行任务的解决方案。 一旦执行任务,我就会向给定的记录器写一条消息,因此通过检查对记录器的调用顺序,我确信我的代码已正确实现。

在这里看到我正在尝试使用的代码:

public class SchedulerFixture
{
    #region Constructors

    public SchedulerFixture()
    {
        LoggerMock = new Mock<ILogger>(MockBehavior.Strict);

        // Setup of other mocks removed for simplicity.
    }

    #endregion
}

public class SequentialTaskExecutorMock : SchedulerFixture
{
    [Fact]
    public void Should_WriteTheCorrectLogEntries_WhenTasksAreExecutedAndNotCancelled()
    {
        // Defines the task that needs to be executed.
        var task = new LongRunningServiceTaskImplementation();

        // Built a sequence in which logs should be created.
        var sequence = new MockSequence();

        LoggerMock.Setup(x => x.Information(It.IsAny<string>(), It.IsAny<string>())).Verifiable();

        LoggerMock.InSequence(sequence).Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")).Verifiable();
        LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)).Verifiable();
        LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStarted)).Verifiable();
        LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, string.Format(CultureInfo.InvariantCulture, LoggingResources.Logger_TaskCompleted, task.TaskName))).Verifiable();
        LoggerMock.InSequence(sequence).Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped)).Verifiable();

        // Setup the mock required for the tests.
        TaskGathererMock.Setup(x => x.GetServiceTasks(LoggerMock.Object)).Returns(() =>
        {
            return new[] { task };
        });

        // Start the scheduler.
        Scheduler.Start(TaskGathererMock.Object, ConfigurationManagerMock.Object);

        // Wait for 5 seconds (this simulates running the service for 5 seconds).
        // Since our tasks execution time takes 4 seconds, all the assigned tasks should have been completed.
        Thread.Sleep(5000);

        // Stop the service. (We assume that all the tasks have been completed).
        Scheduler.Stop();

        LoggerMock.VerifyAll();
    }
}

因此,我测试的第一步是设置日志,然后执行测试本身(这会导致调用记录器),最后我正在验证它。

然而,测试确实经过。

在这种情况下它应该失败,因为以下调用:

LoggerMock.InSequence(sequence).Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk")).Verifiable();

我的代码中没有执行任何内容。

2 个答案:

答案 0 :(得分:1)

虽然这确实感觉像是Moq中的一个错误(请参阅Patrick Quirk提出的问题的第一个评论),但这是一个粗略的想法,你可以做什么。这是一个长期评论&#34;。

创建一个简单的类:

class SequenceTracker
{
    int? state;

    public void Next(int newState)
    {
        if (newState <= state)
            Assert.Fail("Bad ordering there! States should be increasing.");

        state = newState;
    }
}

然后像这样使用它(你自己的代码的修改版本):

public void Should_WriteTheCorrectLogEntries_WhenTasksAreExecutedAndNotCancelled()
{
    // Defines the task that needs to be executed.
    var task = new LongRunningServiceTaskImplementation();

    // USE THE CLASS I PROPOSE:
    var tracker = new SequenceTracker();


    //LoggerMock.Setup(x => x.Information(It.IsAny<string>(), It.IsAny<string>()))

    LoggerMock.Setup(x => x.Information("émqsdlfk", "smdlfksdmlfk"))
        .Callback(() => tracker.Next(10));
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped))
        .Callback(() => tracker.Next(20));
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStarted))
        .Callback(() => tracker.Next(30));
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, string.Format(CultureInfo.InvariantCulture, LoggingResources.Logger_TaskCompleted, task.TaskName)))
        .Callback(() => tracker.Next(40));
    LoggerMock.Setup(x => x.Information(LoggingResources.LoggerTitle, LoggingResources.Logger_ServiceStopped))
        .Callback(() => tracker.Next(50));

    // Setup the mock required for the tests.
    TaskGathererMock.Setup(x => x.GetServiceTasks(LoggerMock.Object)).Returns(() =>
    {
        return new[] { task };
    });

    // Start the scheduler.
    Scheduler.Start(TaskGathererMock.Object, ConfigurationManagerMock.Object);

    // Wait for 5 seconds (this simulates running the service for 5 seconds).
    // Since our tasks execution time takes 4 seconds, all the assigned tasks should have been completed.
    Thread.Sleep(5000);

    // Stop the service. (We assume that all the tasks have been completed).
    Scheduler.Stop();

    // THIS NOW WORKS BECAUSE WE ABANDONED THE 'MockSequence' APPROACH:
    LoggerMock.VerifyAll();
}

当然,如果需要,这可以更加先进。

答案 1 :(得分:0)

受到杰普·斯蒂格·尼尔森(Jeppe Stig Nielsen)的回答的启发,我继续了他的想法,我认为这在某种程度上解决了您的问题(我希望能在Moq中完成)。 这将验证模拟序列的完整执行:

public class SequenceVerifyer
{
    public MockSequence Sequence { get; private set; } = new MockSequence();

    public Action NextCallback()
    {
        var callNo = setupCount++;
        return () => { AssertCallNo(callNo);};
    }
    public void VerifyAll()
    {
        Assert.AreEqual(setupCount, executionCount, $"less calls ({executionCount}) executed than previously set up ({setupCount}).");
    }

    private int executionCount = 0;
    private int setupCount = 0;

    private void AssertCallNo(int expectedCallNo)
    {
        Assert.AreEqual(executionCount, expectedCallNo, $"order of call is wrong. this call is marked with No ({expectedCallNo}), but ({executionCount}) was expected.");
        executionCount++;
    }
}

用法:

public interface IFoo
{
    bool foo(int n);
}

public interface IBar
{
    int bar(string a);
}

[Test]
public void SequenceVerifyer_with_full_sequence()
{
    var fooMock = new Mock<IFoo>(MockBehavior.Strict);
    var barMock = new Mock<IBar>(MockBehavior.Strict);
    var seq = new SequenceVerifyer();

    fooMock.Setup(f => f.foo(3)).Returns(false);
    barMock.Setup(b => b.bar("world")).Returns(4);

    fooMock.InSequence(seq.Sequence).Setup(f => f.foo(4)).Returns(true).Callback(seq.NextCallback());
    barMock.InSequence(seq.Sequence).Setup(b => b.bar("hello")).Returns(2).Callback(seq.NextCallback());
    fooMock.InSequence(seq.Sequence).Setup(f => f.foo(4)).Returns(false).Callback(seq.NextCallback());

    fooMock.Object.foo(3); //non sequence
    fooMock.Object.foo(4);
    barMock.Object.bar("hello");
    barMock.Object.bar("world"); //non sequence
    fooMock.Object.foo(4);

    fooMock.VerifyAll();
    barMock.VerifyAll();
    seq.VerifyAll();
}
相关问题