Testing an async method in ASP MVC Controller with Nunit - c#

I have an ASP.NET MVC application, with an Controller that features asynchronous methods, returning Task<PartialViewResult> object and marked with the async keyword.
This method only takes the data from the database in async mode.
public async Task<PartialViewResult> SomeMethod()
{
using (var unitOfWork = _factory.Create())
{
var result = await unitOfWork.SomeRepository.GetAsync();
return PartialView(result);
};
}
During testing, the stream just freeze in this spot (At run time this code works well):
var models = await unitOfWork.SomeRepository.GetAsync();
This is my test for this method:
public void GetExchange_GetView_OkModelIsViewModel()
{
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository.Setup(x => x.GetAsync(...).Returns(new Task<List<SomeType>>(() => new List<SomeType>()));
//fake UoW returns fake Repository
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.SomeRepository).Returns(mockSomeRepository.Object);
//fake factory create fake UoW
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory);
//Our async method
var result = controller.SomeMethod();
result.Wait();
//---Assert--
}
Question: why is the stream in my method freezes during test execution???
UPDATE
This test begins to work if I replace
var result = await unitOfWork.SomeRepository.GetAsync();
to
var models = unitOfWork.SomeRepository.GetAsync();
models.Start();
models.Wait();
var result = models.Result;
But I don't quite understand why it works like that. Can someone explain?

When testing an async method your test method should be async as well. NUnit can handle this without issue.
[Test]
public async Task GetExchange_GetView_OkModelIsViewModel() {
// ...
var controller = new SomeController(fakeUnitOfWorkFactory);
var result = await controller.SomeMethod(); // call using await
// ...
}

why is the stream in my method freezes during test execution?
There are a few issue with the test.
The initial example was mixing a blocking call (.Wait()) with async calls which lead to a deadlock, hence the hang (deadlock).
The Test should be converted to be async all the way. The test runner should be able to handle that without any problems.
public async Task GetExchange_GetView_OkModelIsViewModel() { ... }
Next the setup of the GetAsync method is not done correctly.
Because the method was not configured to return a completed task that would allow the code to continue, that would also cause a block on that task
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake Repository returns fake Data from DB
var mockSomeRepository = new Mock<ISomeRepository>();
mockSomeRepository
.Setup(x => x.GetAsync())
.Returns(Task.FromResult(fakeData)); // <-- note the correction here
Based on what is needed for the test the setup can be simplified even further to
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync()) //<-- note the full call here
.ReturnsAsync(fakeData); //<-- and also the use of the ReturnsAsync here
The wrong object was also being based to the controller. Pass the object of the mock.
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
Exercising of the method under test should then be awaited.
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
and assertions can be done on the result to verify behavior.
Essentially the problems arose out of how the test was arranged and acted upon. Not the code being tested.
Here is the refactored test
public async Task GetExchange_GetView_OkModelIsViewModel() {
//Arrange
var fakeData = new List<SomeType>() { new SomeType() };
//fake UoF returns fake Data from DB
var mockUnitOfWork = new Mock<IUnitOfWork>();
mockUnitOfWork
.Setup(x => x.SomeRepository.GetAsync())
.ReturnsAsync(fakeData);
//fake factory create fake UoF
var fakeUnitOfWorkFactory = new Mock<UnitOfWorkFactory>();
fakeUnitOfWorkFactory.Setup(x => x.Create()).Returns(mockUnitOfWork.Object);
//Our controller
var controller = new SomeController(fakeUnitOfWorkFactory.Object);
//Act
//Our async method
var result = await controller.SomeMethod() as PartialViewResult;
//---Assert--
result.Should().NotBeNull();
result.Model.Should().NotBeNull();
CollectionAssert.AreEquivalent(fakeData, result.Model as ICollection);
}

Related

How to count the number of times a method is called for a mocked concrete class?

I have a mocked concrete class and when I try to count the number of times a method "x" is invoked in the class, I get an exception. I understand this is not a mocked interface and the method is not overridable. Is there any other way I can count?
I am mocking "RestClient" class of RestSharp. I could actually use the RestClient without mocking it. But I wont be able to tell how many times the "Execute" method of this class was called. I need this to test if the retry mechanism kicked in and tried to make the http call "x" number of times
Mock<RestClient> _mockRestClient = new Mock<RestClient>(mockHttpHandler, true);
//Act
var res = _httpClient.ExecuteTaskWithPolicy(_mockRestClient.Object, _mockRestRequest.Object, policy);
//Assert
_mockRestClient.Verify(x => x.Execute(_mockRestRequest.Object), Times.Exactly(4));
Non-overridable members (here: RestClient.Execute) may not be used in setup / verification expressions.'
RestSharp already deprecated IRestClient interface at v107. If you want to get API execute count, you can follow the guideline. For example, mix RestSharp and MockHttp to verify execute count:
[Test]
public async Task TestAsync()
{
// arrange
using var mockHttp = new MockHttpMessageHandler();
var getFooMockedRequest = mockHttp
.When("http://localhost/api/user/foo")
.Respond("application/json", "{'name' : 'foo'}");
var getBarMockedRequest = mockHttp
.When("http://localhost/api/user/bar")
.Respond("application/json", "{'name' : 'bar'}");
using var client = new RestClient(
new RestClientOptions("http://localhost/api")
{
ConfigureMessageHandler = _ => mockHttp
});
// act
await client.GetAsync(new RestRequest("/user/foo"));
await client.GetAsync(new RestRequest("/user/foo"));
await client.GetAsync(new RestRequest("/user/foo"));
await client.GetAsync(new RestRequest("/user/bar"));
await client.GetAsync(new RestRequest("/user/bar"));
// assert
Assert.AreEqual(3, mockHttp.GetMatchCount(getFooMockedRequest));
Assert.AreEqual(2, mockHttp.GetMatchCount(getBarMockedRequest));
}

How to use A.CallTo with context.T.AddAsync

In my test method, I want to test context.T.AddAsync method and Its output.
Below is the code for the method.
var addRequest = await _context.AddAsync<Request>(requestEntity, cancellationToken);
if (addRequest.State == Microsoft.EntityFrameworkCore.EntityState.Added)
{
Console.WriteLine("Added: " + user.Requests.Count);
saveChanges = await _context.SaveChangesAsync(cancellationToken);
}
else
{
//handle failed AddAsync of the requestEntity
}
Since the result of AddAsync method is being used after that, I have to define a response for that in my test method. Type of addRequest is
Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<Request>
For that, I use A.CallTo, and below is the test method
A.CallTo(() => _request.AddAsync(A<Request>.Ignored, new CancellationToken())).Returns(_addAsyncResponse);
The problem is, I can't figure out how to create the _addAsyncResponse object. I tried with
_addAsyncResponse = new EntityEntry<Request>() { };
and it's not working.
So, after 2 days of researching, I was able to complete this with an in-memory database.
steps
Create an in-memory database.
Call the actual AddAsync method with a Request object.
Assign the result of AddAsync to a var
In the above A.CallTo method, add the above-defined var in the Returns method.
Sample code -
var options = new DbContextOptionsBuilder<DataContext>()
.UseInMemoryDatabase(databaseName: "FakeDB")
.Options;
var context = new DataContext(options);
var addAsyncResult = context.Requests.AddAsync(new Request());
A.CallTo(() => _context.Requests).Returns(_request);
A.CallTo(() => _context.AddAsync(A<Request>.Ignored, new CancellationToken())).Returns(addAsyncResult);
A.CallTo(() => _context.SaveChangesAsync(new CancellationToken())).Returns(1);

How to return a Task<string> using Moq?

I am using XUnit and Moq to test code from my logic layer. My logic layer also communicates with the data layer, so I want to mock the interface to keep my test simple.
I am wondering how I should return a Task<string> when I call the async Task method. My GetOrder method calls GetOrderById but the data layer method returns null.
Edit: I changed my unit test based on all the feedback. It works fine now.
My test:
public async void GetOrder()
{
//Arrange
string expected = "test";
var mock = new Mock<IRepository>();
mock.Setup(arg => arg.GetOrderNameById(It.IsAny<int>())
.Returns(Task.FromResult(expected));
var survey = new SurveyResult(mock.Object);
//Act
string result = await survey.GetOrderNameById(It.IsAny<int>()));
//Assert
Assert.Equal(expected, result);
}
Use Task.FromResult(expected)
mock.Setup(arg => arg.GetScoreByTotalWeighting(value)).Returns(Task.FromResult(expected))
also I'd recomend to avoid value as parameter, when you dont care about that paramter when returning result. You can use It.IsAny<int>(), like that:
mock.Setup(arg => arg.GetScoreByTotalWeighting(It.IsAny<int>())).Returns(Task.FromResult(expected))
Problem that you setting up mock.Setup(arg => arg.GetScoreByTotalWeighting(value)) with value == 0 and then call survey.GetResult(score) with 50. Use It.IsAny<int>() at both places to avoid that problem, or pass same value:
mock.Setup(arg => arg.GetScoreByTotalWeighting(score))
Solution:
public async Task GetResult()
{
//Arrange
string expected = "test";
var mock = new Mock<IRetreiveQuestionRepository>();
mock.Setup(arg => arg.GetScoreByTotalWeighting(It.IsAny<int>()))
.ReturnsAsync(expected);
var survey = new SurveyResult(mock.Object);
//Act
string result = await survey.GetResult(It.IsAny<int>());
//Assert
Assert.Equal(expected, result);
}
You should be able to use the .ReturnsAsync method you're currently using, It's hard to tell why your subject is returning null without knowing the implementation. But I would double check the stubbing to see if you're specifying the value that is indeed passed to the real implementation or if you're missing any other stubbing for another method.
Hope that helps :)

Moq Service in ASP.NET Core

I have some problem with my unit tests. I want to check that my remove method in service is invoke remove method in repository.
My test method:
[Fact]
public async Task Remove_room_async_should_invoke_remove_room_async_on_room_repository()
{
//Arrange
var room = new Room(Guid.NewGuid(), "A-11");
var roomRepositoryMock = new Mock<IRoomRepository>();
var mapperMock = new Mock<IMapper>();
var roomService = new RoomService(roomRepositoryMock.Object, mapperMock.Object);
//Act
await roomService.RemoveAsync(room.RoomId);
//Assert
roomRepositoryMock.Verify(x => x.RemoveAsync(It.IsAny<Room>()), Times.Once());
}
Currently it return me FAIL with exception becouse room with this id doesn't exist.
My repository remove method implementation:
public async Task RemoveAsync(Room room)
{
_rooms.Remove(room);
await Task.CompletedTask;
}
And service
public async Task RemoveAsync(Guid roomId)
{
var room = await _roomRepository.GetOrFailAsync(roomId);
await _roomRepository.RemoveAsync(room);
}
You need to set up the GetOrFailAsync method on your mock IRoomRepository, otherwise the mock is not going to return anything.
So your arrange section becomes:
var room = new Room(Guid.NewGuid(), "A-11");
var roomRepositoryMock = new Mock<IRoomRepository>();
roomRepositoryMock.Setup(r => r.GetOrFailAsync).Returns(room);
var mapperMock = new Mock<IMapper>();
var roomService = new RoomService(roomRepositoryMock.Object, mapperMock.Object);
You also probably want to verify this call as well:
roomRepositoryMock.Verify(r => r.GetOrFailAsync(room.Id), Times.Once());
And consider changing your other verification to match exactly the room you want to delete, rather than IsAny:
roomRepositoryMock.Verify(x => x.RemoveAsync(room), Times.Once());

Proper way of testing ASP.NET Core IMemoryCache

I'm writing a simple test case that tests that my controller calls the cache before calling my service. I'm using xUnit and Moq for the task.
I'm facing an issue because GetOrCreateAsync<T> is an extension method, and those can't be mocked by the framework. I relied on internal details to figure out I can mock TryGetValue instead and get away with my test (see https://github.com/aspnet/Caching/blob/c432e5827e4505c05ac7ad8ef1e3bc6bf784520b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheExtensions.cs#L116)
[Theory, AutoDataMoq]
public async Task GivenPopulatedCacheDoesntCallService(
Mock<IMemoryCache> cache,
SearchRequestViewModel input,
MyViewModel expected)
{
object expectedOut = expected;
cache
.Setup(s => s.TryGetValue(input.Serialized(), out expectedOut))
.Returns(true);
var sut = new MyController(cache.Object, Mock.Of<ISearchService>());
var actual = await sut.Search(input);
Assert.Same(expected, actual);
}
I can't sleep with the fact that I'm peeking into the MemoryCache implementation details and it can change at any point.
For reference, this is the SUT code:
public async Task<MyViewModel> Search(SearchRequestViewModel request)
{
return await cache.GetOrCreateAsync(request.Serialized(), (e) => search.FindAsync(request));
}
Would you recommend testing any differently?
To be honest I would recommend not to test this interaction at all.
I would approach this test case a bit differently: what you really care about is that once your controller retrieved data from your ISearchService it shouldn't request the data again and should return the result from the previous call.
The fact that an IMemoryCache is used behind the scenes is just an implementation detail. I wouldn't even bother setting up a test double for it, I would just use an instance of the Microsoft.Extensions.Caching.Memory.MemoryCache object.
My new test would look something like this:
[Theory]
public async Task GivenResultAlreadyRetrieved_ShouldNotCallServiceAgain()
{
// Arrange
var expected = new MyViewModel();
var cache = new MemoryCache(new MemoryCacheOptions());
var searchService = new Mock<ISearchService>();
var input = new SearchRequestViewModel();
searchService
.SetupSequence(s => s.FindAsync(It.IsAny<SearchRequestViewModel>()))
.Returns(Task.FromResult(expected))
.Returns(Task.FromResult(new MyViewModel()));
var sut = new MyController(cache, searchService.Object);
// Act
var resultFromFirstCall = await sut.Search(input);
var resultFromSecondCall = await sut.Search(input);
// Assert
Assert.Same(expected, resultFromFirstCall);
Assert.Same(expected, resultFromSecondCall);
}

Categories