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());
Related
I'm trying to test the behaviour of an Orchestrator function and want to try to only mock calls that depends on external sources. This is to test that the combination of steps aka. the behaviour of the Orchestrator function aqually works.
I'm not interested in seperated tests for every small activity trigger that gets added to the function since the goal is to verify the final result.
So questions are:
Is this even possible?
Can I get the Result in setup and return it as an input in another activity trigger setup?
Current unfinished test method:
[Fact]
public async Task TestOrchestrator()
{
// Setup
var fileContent = Encoding.UTF8.GetBytes("this is some filecontent 😀");
var encodedFileContent = Encoding.Latin1.GetString(fileContent);
const string runId = "changeme";
var loggerMock = new Mock<ILogger>();
var clientMock = new Mock<IDurableOrchestrationContext>();
clientMock
.Setup(context => context.CallActivityAsync<byte[]>(nameof(GetBlobContentAsByteArray), runId))
.ReturnsAsync(fileContent);
// I want EncodeToISO88591 to do it's thing instead of me having to return a fake value.
// clientMock
// .Setup(context => context.CallActivityAsync<string>(nameof(EncodeToISO88591), new DataObject<byte[]>(fileContent)))
// .ReturnsAsync(encodedFileContent);
// I want this to consume the value of EncodeToISO88591, not a fake one.
clientMock
.Setup(context => context.CallActivityAsync<bool>(nameof(UploadToSftp), encodedFileContent))
.ReturnsAsync(true);
//Act
await RunOrchestrator(clientMock.Object, loggerMock.Object);
}
OrchestratorFunction:
public record DataObject<T>(T Data);
const string OrchestratorFunctionName = nameof(RunOrchestrator);
const string EncodeFunctionName = nameof(EncodeToISO88591);
const string GetBlobContentFunctionName = nameof(GetBlobContentAsByteArray);
const string UploadToSftpFunctionName = nameof(UploadToSftp);
[FunctionName(OrchestratorFunctionName)]
public static async Task RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger)
{
if (!context.IsReplaying)
logger.LogInformation("Starting Orchestration");
var runId = context.GetInput<string>();
byte[] blobContent = await context.CallActivityAsync<byte[]>(GetBlobContentFunctionName, runId);
string encodedContent = await context.CallActivityAsync<string>(EncodeFunctionName, new DataObject<byte[]>(blobContent));
bool sftpUploadSuccess = await context.CallActivityAsync<bool>(UploadToSftpFunctionName, encodedContent);
if (!sftpUploadSuccess)
logger.LogError("Failed to upload to sftp");
if (!context.IsReplaying)
logger.LogInformation($"Completed Orchestration of instance {context.InstanceId}");
}
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);
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);
}
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);
}
[Edit (May 2020)] - This issue has been reportedly addressed in newer releases of NUnit. Please see Nunit.ThrowsAsync. (Ref this answer, thanks #James-Ross)
I have a controller UserController with this action
// GET /blah
public Task<User> Get(string domainUserName)
{
if (string.IsNullOrEmpty(domainUserName))
{
throw new ArgumentException("No username specified.");
}
return Task.Factory.StartNew(
() =>
{
var user = userRepository.GetByUserName(domainUserName);
if (user != null)
{
return user;
}
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("{0} - username does not exist", domainUserName)));
});
}
I am trying to write a test for the case where I throw a 404 exception.
Here is what I have tried, with the output -
1)
[Test]
public void someTest()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
Assert.That(async () => await userController.Get("foo"), Throws.InstanceOf<HttpResponseException>());
}
Result
Test Failed
Expected: instance of <System.Web.Http.HttpResponseException>
But was: no exception thrown
[Test]
public void someTest()
{
var mockUserRepository = new Mock();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var httpResponseException = Assert.Throws<HttpResponseException>(() => userController.Get("foo").Wait());
Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
Result
Test failed
Expected: <System.Web.Http.HttpResponseException>
But was: <System.AggregateException> (One or more errors occurred.)
[Test]
public void someTest()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var httpResponseException = Assert.Throws<HttpResponseException>(async () => await userController.Get("foo"));
Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
Result
Test Failed
Expected: <System.Web.Http.HttpResponseException>
But was: null
[Test]
[ExpectedException(typeof(HttpResponseException))]
public async void ShouldThrow404WhenNotFound()
{ var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var task = await userController.Get("foo");
}
Result
Test passes
Questions -
Why would Assert.Throws not handle HttpResponseException, when ExpectedException does?
I don't want to just test that exception is thrown. I want to assert on the Status Code of the response. What's the way to do this?
Any comparision on these behaviour and its cause(s) would be great!
You're seeing problems due to async void.
In particular:
async () => await userController.Get("foo") is converted into TestDelegate, which returns void, so your lambda expression is treated as async void. So the test runner will begin executing the lambda but not wait for it to complete. The lambda returns before Get completes (because it's async), and the test runner sees that it returned without an exception.
Wait wraps any exceptions in an AggregateException.
Again, the async lambda is being treated as async void, so the test runner is not waiting for its completion.
I recommend you make this async Task rather than async void, but in this case the test runner does wait for completion, and thus sees the exception.
According to this bug report, there is a fix for this coming in the next build of NUnit. In the meantime, you can build your own ThrowsAsync method; an example for xUnit is here.
I'm not sure when it was added, but the current version of Nunit (3.4.1 at time of writing) includes a ThrowsAsync method
see https://github.com/nunit/docs/wiki/Assert.ThrowsAsync
Example:
[Test]
public void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var exception = Assert.ThrowsAsync<HttpResponseException>(() => userController.Get("foo"));
Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
This blog talks about problems similar to mine.
I followed the recommendation proposed there, and have a test like this -
[Test]
public void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
var aggregateException = Assert.Throws<AggregateException>(() => userController.Get("foo").Wait());
var httpResponseException = aggregateException.InnerExceptions
.FirstOrDefault(x => x.GetType() == typeof(HttpResponseException)) as HttpResponseException;
Assert.That(httpResponseException, Is.Not.Null);
Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
I am not too pleased with it, but this works.
EDIT 1
Inspired by #StephenCleary, I added a static helper class that does the asserts that I am looking for. It looks like this -
public static class AssertEx
{
public static async Task ThrowsAsync<TException>(Func<Task> func) where TException : class
{
await ThrowsAsync<TException>(func, exception => { });
}
public static async Task ThrowsAsync<TException>(Func<Task> func, Action<TException> action) where TException : class
{
var exception = default(TException);
var expected = typeof(TException);
Type actual = null;
try
{
await func();
}
catch (Exception e)
{
exception = e as TException;
actual = e.GetType();
}
Assert.AreEqual(expected, actual);
action(exception);
}
}
I can now have a test like -
[Test]
public async void ShouldThrow404WhenNotFound()
{
var mockUserRepository = new Mock<IUserRepository>();
mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User));
var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() };
Action<HttpResponseException> asserts = exception => Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
await AssertEx.ThrowsAsync(() => userController.Get("foo"), asserts);
}
If you await a Task then Exceptions that are thrown are aggregated into AggregateException. You can inspect the inner exceptions of AggregateException. This could be the reason why you case 2 does not work.
Unhandled exceptions that are thrown by user code that is running inside a task are propagated back to the joining thread, except in certain scenarios that are described later in this topic. Exceptions are propagated when you use one of the static or instance Task.Wait or Task.Wait methods, and you handle them by enclosing the call in a try-catch statement. If a task is the parent of attached child tasks, or if you are waiting on multiple tasks, then multiple exceptions could be thrown. To propagate all the exceptions back to the calling thread, the Task infrastructure wraps them in an AggregateException instance. The AggregateException has an InnerExceptions property that can be enumerated to examine all the original exceptions that were thrown, and handle (or not handle) each one individually. Even if only one exception is thrown, it is still wrapped in an AggregateException.
Link to MSDN
I have a similar issue that you have in scenario 3 Test case failed due to following result
Expected: <UserDefineException>
But was: null
by using Assert.ThrowAsync<> problem gets solved
My Web API action method and Unit test case method as below
public async Task<IHttpActionResult> ActionMethod(RequestModel requestModel)
{
throw UserDefineException();
}
[Test]
public void Test_Contrller_Method()
{
Assert.ThrowsAsync<UserDefineException>(() => _controller.ActionMethod(new RequestModel()));
}
This is an example from documentation:
var ex = Assert.ThrowsAsync<ArgumentException>(async () => await MethodThatThrows());
Use ThrowsAsync
Use async / await
https://docs.nunit.org/articles/nunit/writing-tests/assertions/classic-assertions/Assert.ThrowsAsync.html