Proper way of testing ASP.NET Core IMemoryCache - c#

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);
}

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));
}

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.

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());

Testing an async method in ASP MVC Controller with Nunit

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);
}

Categories