使用Moq模拟单元测试中的执行延迟

Jul*_*ing 11 c# unit-testing mstest moq

我正在尝试测试以下内容:

protected IHealthStatus VerifyMessage(ISubscriber destination)
{
    var status = new HeartBeatStatus();

    var task = new Task<CheckResult>(() =>
    {
        Console.WriteLine("VerifyMessage(Start): {0} - {1}", DateTime.Now, WarningTimeout);
        Thread.Sleep(WarningTimeout - 500);
        Console.WriteLine("VerifyMessage(Success): {0}", DateTime.Now);
        if (CheckMessages(destination))
        {
            return CheckResult.Success;
        }

        Console.WriteLine("VerifyMessage(Pre-Warning): {0} - {1}", DateTime.Now, ErrorTimeout);
        Thread.Sleep(ErrorTimeout - 500);
        Console.WriteLine("VerifyMessage(Warning): {0}", DateTime.Now);
        if (CheckMessages(destination))
        {
            return CheckResult.Warning;
        }

        return CheckResult.Error;
    });

    task.Start();

    task.Wait();
    status.Status = task.Result;

    return status;
}
Run Code Online (Sandbox Code Playgroud)

通过以下单元测试:

public void HeartBeat_Should_ReturnWarning_When_MockReturnsWarning()
{
    // Arrange
    var heartbeat = new SocketToSocketHeartbeat(_sourceSubscriber.Object, _destinationSubscriber.Object);
    heartbeat.SetTaskConfiguration(this.ConfigurationHB1ToHB2_ValidConfiguration());

    // Simulate the message being delayed to destination subscriber.
    _destinationSubscriber.Setup(foo => foo.ReceivedMessages).Returns(DelayDelivery(3000, Message_HB1ToHB2()));

    // Act
    var healthStatus = heartbeat.Execute();

    // Assert
    Assert.AreEqual(CheckResult.Warning, healthStatus.Status);
}
Run Code Online (Sandbox Code Playgroud)

Message_HB1ToHB2()只返回一个字符串,"Delay Delivery"方法是

private List<NcsMessage> DelayDelivery(int delay, string message)
{
    var sent = DateTime.Now;
    var msg = new NcsMessage()
    {
        SourceSubscriber = "HB1",
        DestinationSubscriber = "HB2",
        SentOrReceived = sent,
        Message = message
    };

    var messages = new List<NcsMessage>();
    messages.Add(msg);

    Console.WriteLine("DelayDelivery: {0}", DateTime.Now);
    Thread.Sleep(delay);
    Console.WriteLine("DelayDelivery: {0}", DateTime.Now);

    return messages;
}
Run Code Online (Sandbox Code Playgroud)

我使用Moq作为模拟框架,使用MSTest作为测试框架.每当我运行单元测试时,我得到以下输出:

DelayDelivery: 04/04/2013 15:50:33
DelayDelivery: 04/04/2013 15:50:36
VerifyMessage(Start): 04/04/2013 15:50:36 - 3000
VerifyMessage(Success): 04/04/2013 15:50:38
Run Code Online (Sandbox Code Playgroud)

除了在上面的方法中使用Thread.Sleep的明显的"代码味道"之外,单元测试的结果不是我想要完成的.

任何人都可以建议一种更好/更准确的方法来使用Moq框架来模拟消息"传递"的延迟.我遗漏了一些"胶水"代码,只包括相关部分.让我知道,如果我遗漏的东西阻止你能够理解这个问题.

Ada*_*ger 34

如果你想让Moq模拟只是坐下来什么也不做,你可以使用回调:

Mock<IFoo> mockFoo = new Mock<IFoo>();
mockFoo.Setup(f => f.Bar())
       .Callback(() => Thread.Sleep(1000))
       .Returns("test");

string result = mockFoo.Object.Bar(); // will take 1 second to return

Assert.AreEqual("test", result);
Run Code Online (Sandbox Code Playgroud)

我在LinqPad中尝试过,如果你调整Thread.Sleep()执行时间会有所不同.

  • 这对我来说似乎不起作用,我认为回调是在Moq返回结果后执行的 (6认同)

Sus*_*eng 8

设置模拟时,您可以告诉线程在返回函数中休眠:

Mock<IMyService> myService = new Mock<IMyService>();

myService.Setup(x => x.GetResultDelayed()).Returns(() => {
    Thread.Sleep(100);
    return "result";
});
Run Code Online (Sandbox Code Playgroud)

  • 确切地。这比公认的解决方案有优势,如果 GetResultDelayed 的类型为 Task&lt;string&gt;: myService.Setup(x =&gt; x.GetResultDelayed()).Returns(async () =&gt; { await Task.Delay(100); return "result"; }); (2认同)

Ben*_*ica 7

如果运行异步代码,Moq 能够通过第二个参数延迟响应 TimeSpan

mockFooService
    .Setup(m => m.GetFooAsync())
    .ReturnsAsync(new Foo(), TimeSpan.FromMilliseconds(500)); // Delay return for 500 milliseconds.
Run Code Online (Sandbox Code Playgroud)

如果每次调用方法都需要指定不同的延迟,可以使用.SetupSequencelike

mockFooService
    .SetupSequence(m => m.GetFooAsync())
    .Returns(new Foo())
    .Returns(Task.Run(async () => 
    {
        await Task.Delay(500) // Delay return for 500 milliseconds.
        return new Foo();
    })

Run Code Online (Sandbox Code Playgroud)