Using Moq to mock an asynchronous method for a unit test - c#

I am testing a method for a service that makes a Web API call. Using a normal HttpClient works fine for unit tests if I also run the web service (located in another project in the solution) locally.
However when I check in my changes the build server won't have access to the web service so the tests will fail.
I've devised a way around this for my unit tests by creating an IHttpClient interface and implementing a version that I use in my application. For unit tests, I make a mocked version complete with a mocked asynchronous post method. Here's where I have run into problems. I want to return an OK HttpStatusResult for this particular test. For another similar test I will be returning a bad result.
The test will run but will never complete. It hangs at the await. 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 is the method I am trying to test:
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 my unit test method:
[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 help.

You're creating a task but never starting it, so it's never completing. However, don't just start the task - instead, change to using Task.FromResult<TResult> which will give you a task which has already completed:
...
.Returns(Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.OK)));
Note that you won't be testing the actual asynchrony this way - if you want to do that, you need to do a bit more work to create a Task<T> that you can control in a more fine-grained manner... but that's something for another day.
You might also want to consider using a fake for IHttpClient rather than mocking everything - it really depends on how often you need it.

Recommend #Stuart Grassie's answer above.
var moqCredentialMananger = new Mock<ICredentialManager>();
moqCredentialMananger
.Setup(x => x.GetCredentialsAsync(It.IsAny<string>()))
.ReturnsAsync(new Credentials() { .. .. .. });

With Mock.Of<...>(...) for async method you can use Task.FromResult(...):
var client = Mock.Of<IHttpClient>(c =>
c.PostAsync(It.IsAny<Uri>(), It.IsAny<HttpContent>()) == Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))
);

Try using ReturnsAsync.
In asynchronous methods it works, I believe the basis to solve your problem should be similar.
_mocker.GetMock<IMyRepository>()
.Setup(x => x.GetAll())
.ReturnsAsync(_myFakeListRepository.GetAll());

Related

Delay Returns with NSubstitute

I have an interface IDiscosClient, for testing/demo purposes while I'm developing the app, I want a mock to return a new model when the .GetSingle<T>() method is called with a random delay of between 1 and 5 seconds. This is mostly so I can see that all of my various loading spinner components and whatnot work.
So, I thought I'd be able to do something like this:
Fixture fixture = new();
fixture.Customize(new DiscosModelFixtureCustomizationNoLinks());
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
DiscosObject obj = fixture.Create<DiscosObject>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
return client;
});
However, while there seems to be a delay when I first call the method, once this has resolved, it just seems to return the completed task with the same model in it every time I call it for that IDiscosClient instance.
Is there a simple enough way to accomplish this?
So the issue is that the code above only creates a fresh Task the first time and then returns the same one (which has already completed) each subsequent time.
To fix this, we can either change the code above to:
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(_ => Task.Delay(Random.Shared.Next(1000,5000)).ContinueWith(_ => obj));
Or, for legibilities sake, we can extract it into a method and make the whole code block:
builder.Services.AddTransient(_ =>
{
IDiscosClient client = Substitute.For<IDiscosClient>();
client.GetSingle<DiscosObject>(Arg.Any<string>()).Returns(GetDiscosObject);
return client;
});
async Task<DiscosObject> GetDiscosObject(CallInfo _)
{
await Task.Delay(Random.Shared.Next(1000, 5000));
return fixture.Create<DiscosObject>();
}

Net Core Moq - Persist Httpclient when running the same function multiple times within the same unit test

I have this unit test:
[Fact(DisplayName = "Http test")]
public async Task Http_Test_Should_Return_Something()
{
var model = new Test();
model.test1= "tt";
var messageHandler = new Mock<HttpMessageHandler>();
messageHandler.ClientBuilder(url, delegate () { return JsonConvert.SerializeObject(model); });
var httpClient = new HttpClient(messageHandler.Object, false);
var func = new Class(httpClient, _propertiesSetOnContruct);
//RUN FIRST TIME TO SET THE PRIVATE FIELDS AND DO THE NORMAL PROPERTY GATHERING
await func.RunLogic();
//RUN THE SECOND TIME TO GET THE DICT VALUE SET AND DONE AND RUN THE REST OF THE LOGIC
await func.RunLogic();
}
This is my ClientBuilder Code in which I setup the handler to run the function:
public static void TokenClientBuilder(this Mock<HttpMessageHandler> handler, string requestUrl, Func<string> function)
{
handler.Protected()
.As<IHttpMessageHandlerMock>()
.Setup(x => x.SendAsync(
It.Is<HttpRequestMessage>(r =>
r.Method == HttpMethod.Post &&
r.RequestUri == new Uri(requestUrl)),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new HttpResponseMessage()
{
Content = new StringContent(function(),
Encoding.UTF8,
"application/json")
});
}
So, to resume the code that I have there in the unit test, I basically need to cover all the code of the class that I am trying to check, the problem is that I need to run it 2 times within the same unit test, since the Dictionary that I want to populate can only be populated when I run the first time and the second check will only happen when the user goes again to the class.
Since the Httpclient is disposed when I go for the 2nd round I lose my content and I end up with a null value and when I try to do a Json Convert it obviously causes an exception.
I even thought of mocking the dictionary within the class, but it is private and I can't access it.
My question here is, is there any way, shape or form to mock the MessageHandler in order for the Httpclient to persist in the 2nd round of the unit test or do I need to do something else?
The code that I am trying to test is not relevant for here because it won't change and the constructor props and the props of the class are private, so nothing to do there.

moq for mediator using generics in unit testing

I use cqrs and mediatr in .net core 3 webapi project. I want to unit test controller methods behavior, so when the method is called, mediator does it work as expected. I wrote testing code for one method and it works fine. Many code is omitted for brevity, I only used code which causes the error. So, working test:
var mockScrapedAd = ScrapedAd.CreateMockAd(DateTime.Now); //result of controller method
mediatrMock.Setup(m =>
m.Send(It.IsAny<QueryDetails>(),
It.IsAny<CancellationToken>())).
ReturnsAsync(() => mockScrapedAd); //mock for mediator,
// so when something with QueryDetails type is called, it should return result from above
var ret = await controller.Details(0); //should send QueryDetails via Mediatr
Assert.Equal(mockScrapedAd, ret.Value); //all good?
mediatrMock.Verify(x => x.Send(It.IsAny<QueryDetails>(), //was it called at all?
It.IsAny<CancellationToken>()));
This test works, but I will have to duplicate code for each controller method. The only thing that changes here is type of mediatr request (QueryDetails here) and mock for return.
So I tried to use generic method like here:
async Task TestBase<TK,K>(TK expectedResult,Func<Task<ActionResult<TK>>> callControllerMethod)
{
mediatrMock.Setup(m =>
m.Send(It.IsAny<K>(),
It.IsAny<CancellationToken>())).ReturnsAsync(() => expectedResult);
var ret = await callControllerMethod();
Assert.Equal(expectedResult, ret.Value);
mediatrMock.Verify(x => x.Send(It.IsAny<K>(),
It.IsAny<CancellationToken>()));
}
And the test suite will be easier:
var mockScrapedAd = ScrapedAd.CreateMockAd(DateTime.Now);
await TestBase<ScrapedAd,QueryDetails>(mockScrapedAd, async () => await controller.Details(0));
Basically all I changed is moved QueryDetails inside generic method. Mediator fails to recognize it and response is not called. What's wrong here?

How could I throw an exception using Fluent Assertions?

I am using a client to interact with CloudMQTT API. I am trying to create a user but after trying the code provided below, I was not able to create a user. When using the code provided within the Github repository for this project, I noticed that I am unable to make use of a ShouldThrow() method (apparently it should be provided by Fluent Assertions).
I did find a post on StackOverflow which looked very similar to the problem I am having. In the question is mentioned that FluentAssertions does not support async methods. In the example code for the client, however, I can see that the ShouldThrow() method is used regardless of this fact.
How could I get the ShoudldThrow() to work or do I even need it to work (because I think it is only supposed to be required in this code if you are applying unit testing)?
This is what a tried so far:
public static async void CreateCloudUser(ICloudMqttApi client)
{
var users = await client.GetUsers();
Console.WriteLine($"Creating a user. Current users available: {users.Count}");
var expectedUser = new NewUser
{
Password = $"{Guid.NewGuid()}",
Username = $"staging-{Guid.NewGuid()}",
};
var createUserResponse = await client.CreateUser(expectedUser);
createUserResponse.IsSuccessStatusCode.Should().BeTrue();
var actual = await client.GetUser(expectedUser.Username);
actual.Should().NotBeNull();
actual.Username.Should().Be(expectedUser.Username);
//users.Should().Contain(u => u.Username == expectedUser.Username); // <-- This throws an exception as well, but not of importance for this specific question.
Func<Task> verifyUser = async () => await client.GetUser(expectedUser.Username);
verifyUser.ShouldThrow<ApiException>() // <-- Not recognized
.And.StatusCode.Should().Be(HttpStatusCode.NotFound);
Console.WriteLine($"Created a user. Current users available: {users.Count}");
}
The client is defined in the way as provided in the documentation for the client right before calling the method:
var client = CloudMqttApi.GetInstance("username", "password");
The user count will result in the same number before and after executing the method (which obviously should have incremented).
Given the asynchronous nature of the shown code, the syntax should be
//...
var deleteResponse = await client.DeleteUser(expectedUser.Username);
deleteResponse.IsSuccessStatusCode.Should().BeTrue();
Func<Task> verifyUser = async () => await client.GetUser(expectedUser.Username);
var exceptionAssertion = await verifyUser.Should().ThrowAsync<ApiException>();
exceptionAssertion.And.StatusCode.Should().Be(HttpStatusCode.NotFound);
//...
Reference FluentAssertions: Exceptions
Also avoid using async void. Have the function return Task
public static async Task CreateCloudUser(ICloudMqttApi client) {
//...
}
Reference Async/Await - Best Practices in Asynchronous Programming

Testing Task-based WCF call

I am converting the WCF calls I am making in an application to run asynchronously to ensure the GUI is responsive while it gets data. Mostly I am using these methods to populate the properties of a ViewModel.
For instance, here's my old and new code:
private async Task LoadDataItems()
{
//DataItems = Service.SelectDataItems();
DataItems = await Service.SelectDataItemsAsync();
}
Also, here's some test code using RhinoMocks:
//Doesn't set DataItems when LoadDataItems() is called
myWcfServiceClient.Stub(async client => await client.SelectDataItemsAsync()).Return(new Task<List<DataItemDto>>(() => new List<DataItemDto> { testDataItem }));
//NullReferenceException on SelectDataItemsAsync()
myWcfServiceClient.Stub(client => client.SelectDataItemsAsync().Result).Return(new List<DataItemDto> { testDataItem });
Basically, in my unit test, either DataItems isn't set or I get a NullReferenceException trying to fake out the result. This is probably as much a question on RhinoMocks than anything...
In RhinoMocks, you define the Result on the Task-based operation with Task.FromResult(...)
So, my test code would set up the result as follows:
myWcfServiceClient.Stub(client => client.SelectDataItemsAsync()).Return(Task.FromResult(new List<DataItemDto> { testDataItem }));
Simple and works great!

Categories