Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xUnit and Moq do not support async - await keywords

Tags:

I am trying to discover how to apply the async and await keywords to my xUnit tests. I am using xUnit 1.9 and Async CTP 1.3. Here is my test case

I have an interface which specifies one asynchronous method call

public interface IDoStuffAsync
{
    Task AnAsyncMethod(string value);
}

I have a class which consumes the interface and calls the async method

public class UseAnAsyncThing
{
    private readonly IDoStuffAsync _doStuffAsync;

    public UseAnAsyncThing(IDoStuffAsync doStuffAsync)
    {
        _doStuffAsync = doStuffAsync;
    }

    public async Task DoThatAsyncOperation(string theValue)
    {
        await _doStuffAsync.AnAsyncMethod(theValue);
    }
}

In my tests I wish to check that the method DoThatAsyncOperation is calling the method with the correct value so I mock the interface and use the Moq to verify the call

    [Fact]
    public async void The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        await sut.DoThatAsyncOperation("test");

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

This test is using the async and await keywords. When it runs it erroneously passes as Moq should assert that the verify fails. Any code after the call to sut.DoThatAsyncOperation("test"); does not run

    [Fact]
    public void This_will_work_and_assert_the_reslt()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        sut.DoThatAsyncOperation("test").ContinueWith(y => { });

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

This test is setup without the await and async keywords and passes fine.

Is this expected behavior for xUnit and Moq?


Update

Thanks for Stephen's comment I managed to fix the first test by making two changes. The test now returns a Task instead of void and the Mock also returns a Task.

    [Fact]
    public async Task The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())).ReturnAsync(true);

        await sut.DoThatAsyncOperation("test");

        // This now fails as it should
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }
like image 544
Keith Bloom Avatar asked Jun 27 '12 16:06

Keith Bloom


1 Answers

Change your unit test method to return Task instead of void, and it should work. Support for async void unit tests is being considered for a future release.

I describe in detail why async unit tests don't work by default on my blog. (My blog examples use MSTest, but the same problems existed in every other test runner, including xUnit pre-1.9).

like image 101
Stephen Cleary Avatar answered Sep 29 '22 11:09

Stephen Cleary