Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delay in Moq return for async method not delaying

In my unit test, I'm trying to mock out the Run() async method from my interface IPipeline and simulate a delay, where it is called in the class PipelineScheduler

public interface IPipeline
{
    Task Run();
}

Test Moq:

[SetUp]
public void SetUp()
{
    _mockPipeline = new Mock<IPipeline>();

    _mockPipeline.Setup(x => x.Run()).Returns(async () =>
    {
        await Task.Delay(3000);
    });
    _scheduler = new PipelineScheduler(_mockPipeline.Object);
}

However when I run the test and debug where the mocked method is called

await _pipeline.Run().ConfigureAwait(false);

there is no delay and execution continues after this line immediately.

But if I replace the mock with a stub class, the delay works fine.

private class MockPipeline : IPipeline
{
    public async Task Run()
    {
        await Task.Delay(3000);
    }
}

[SetUp]
public void SetUp()
{
    _mockPipeline = new MockPipeline();
    _scheduler = new PipelineScheduler(_mockPipeline);
}

So I suppose the question is what's different from how I'm creating the delay with moq vs my stubbed class?

like image 540
roverred Avatar asked Jan 03 '18 10:01

roverred


1 Answers

The difference is that the setup is being configured incorrectly.

Returning a Task i.e:.Returns(Task.Delay(3000)); is all that is needed for the the setup to behave a desired. The previous setup is a fire and forget async void which is why the previous example did not wait and continued immediately.

The following minimal example demonstrates how the mock should have been setup

[TestClass]
public class MyTestClass {
    [TestMethod]
    public async Task MyTestMethod() {
        //Arrange
        var _mockPipeline = new Mock<IPipeline>();

        _mockPipeline.Setup(x => x.Run()).Returns(Task.Delay(3000)).Verifiable();

        var sut = new PipelineScheduler(_mockPipeline.Object);

        //Act
        await sut.MethodUnderTest();

        //Assert
        _mockPipeline.Verify();
    }
}

public interface IPipeline {
    Task Run();
}

public class PipelineScheduler {
    private IPipeline _pipeline;

    public PipelineScheduler(IPipeline pipeline) {
        this._pipeline = pipeline;
    }

    public async Task MethodUnderTest() {

        await _pipeline.Run().ConfigureAwait(false);

    }
}

When exercised, the test delays for the configured 3 seconds.

like image 71
Nkosi Avatar answered Sep 30 '22 21:09

Nkosi