Coder Perfect

For a unit test, Moq was used to fake an asynchronous method.

Problem

I’m putting a method for a service that calls a Web API to the test. If I additionally run the web service (placed in another project in the solution) locally, using a regular HttpClient works fine for unit tests.

When I check in my changes, however, the build server will be unable to access the web service, causing the tests to fail.

I’m creating a version that I’ll utilize in my app. I create a mocked version for unit testing, replete with a mocked asynchronous post method. This is where I’ve run into issues. For this test, I’d like to return an OK HttpStatusResult. I’ll be returning a negative result for a similar test.

The test will continue to run, but it will never finish. It’s stuck at the waiting stage. I am new to asynchronous programming, delegates, and Moq itself and I’ve been searching SO and google for a while learning new things but I still can’t seem to get past this problem.

Here’s the procedure I’m attempting to validate:

public async Task<bool> QueueNotificationAsync(IHttpClient client, Email email)
{
    // do stuff
    try
    {
        // The test hangs here, never returning
        HttpResponseMessage response = await client.PostAsync(uri, content);

        // more logic here
    }
    // more stuff
}

Here’s how I do unit testing:

[TestMethod]
public async Task QueueNotificationAsync_Completes_With_ValidEmail()
{
    Email email = new Email()
    {
        FromAddress = "bob@example.com",
        ToAddress = "bill@example.com",
        CCAddress = "brian@example.com",
        BCCAddress = "ben@example.com",
        Subject = "Hello",
        Body = "Hello World."
    };
    var mockClient = new Mock<IHttpClient>();
    mockClient.Setup(c => c.PostAsync(
        It.IsAny<Uri>(),
        It.IsAny<HttpContent>()
        )).Returns(() => new Task<HttpResponseMessage>(() => new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

    bool result = await _notificationRequestService.QueueNotificationAsync(mockClient.Object, email);

    Assert.IsTrue(result, "Queue failed.");
}

What am I doing wrong?

Thank you for your assistance.

Asked by mvanella

Solution #1

You create a task but never start it, thus it never gets finished. However, instead of starting the task, switch to Task mode. This will offer you a job that has already been completed: FromResultTResult>

...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));

You won’t be testing the actual asynchrony this way; if you want to do that, you’ll need to put in a little more effort to develop a TaskT> that you can manage more finely… but that’s a topic for another day.

Instead of mimicking everything, you might want to try using a fake for IHttpClient – it all depends on how often you require it.

Answered by Jon Skeet

Solution #2

Recommend @Stuart Grassie’s previous response.

var moqCredentialMananger = new Mock<ICredentialManager>();
moqCredentialMananger
                    .Setup(x => x.GetCredentialsAsync(It.IsAny<string>()))
                    .ReturnsAsync(new Credentials() { .. .. .. });

Answered by DineshNS

Solution #3

You can use Task.FromResult(…) with Mock.Of…>(…) for async methods:

var client = Mock.Of<IHttpClient>(c => 
    c.PostAsync(It.IsAny<Uri>(), It.IsAny<HttpContent>()) == Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))
);

Answered by bside

Post is based on https://stackoverflow.com/questions/20859639/using-moq-to-mock-an-asynchronous-method-for-a-unit-test