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 created an IHttpClient interface and implemented a version that I use in my application to get around this for my unit tests. I create a mocked version for unit testing, replete with a mocked asynchronous post method. This is where I’ve run across 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’m new to asynchronous programming, delegates, and Moq, and I’ve been learning new stuff from SO and Google for a long, but I still can’t seem to get over 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
}
My unit testing procedure is as follows:
[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.");
}
I’m not sure what I’m doing incorrectly.
I appreciate 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
I’d recommend @Stuart Grassie’s 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
Solution #4
ReturnsAsync is a good option. It works in asynchronous techniques, and I believe the foundation for solving your problem should be the same.
_mocker.GetMock<IMyRepository>()
.Setup(x => x.GetAll())
.ReturnsAsync(_myFakeListRepository.GetAll());
Answered by Cleber Spirlandeli
Post is based on https://stackoverflow.com/questions/20859639/using-moq-to-mock-an-asynchronous-method-for-a-unit-test