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