desautelsj desautelsj - 6 months ago 44
C# Question

Mocking Task.Delay

I have a method with the following line:

await Task.Delay(waitTime).ConfigureAwait(false);

I there a good strategy to avoid actually waiting the few seconds when unit testing and instead verify that we tried to wait a specific number of seconds.

For instance, is there a way to inject an additional parameter into my method like in this (contrived) example where I inject a mocked object of a fictitious

// Arrange
var mockWait = new Mock<ITaskWaiter>(MockBehavior.Strict);
mockWait.Setup(w => w.Delay(It.Is<TimeSpan>(t => t.TotalSeconds == 2)));

// Act

// Assert


You can define a "delayer" interface like this:

public interface IAsyncDelayer
    Task Delay(TimeSpan timeSpan);

And then you can provide the following implementation for production code:

public class AsyncDelayer : IAsyncDelayer
    public Task Delay(TimeSpan timeSpan)
        return Task.Delay(timeSpan);

Now, your class would look something like this:

public class ClassUnderTest
    private readonly IAsyncDelayer asyncDelayer;

    public ClassUnderTest(IAsyncDelayer asyncDelayer)
        this.asyncDelayer = asyncDelayer;

    public async Task<int> MethodUnderTest()
        await asyncDelayer.Delay(TimeSpan.FromSeconds(2));

        return 5;

This is basic application of Dependency Injection. Basically, we extracted the logic of asynchronously waiting to a different class and created an interface for it to enable polymorphism.

In production, you would compose your object like this:

var myClass = new ClassUnderTest(new AsyncDelayer());

Now, in your test you can create a fake delayer that returns immediately like this:

public async Task TestMethod1()
    var mockWait = new Mock<IAsyncDelayer>();

    mockWait.Setup(m => m.Delay(It.IsAny<TimeSpan>())).Returns(Task.FromResult(0));

    var sut = new ClassUnderTest(mockWait.Object);

    var result = await sut.MethodUnderTest();

    Assert.AreEqual(5, result);