Nunit testing using FirstOrDefaultAsync() - c#

I have the following method:
public async Task<string> GetDescription(int codeId)
{
var Codes = Context.Codes.Where(x => x.Id == codeId).FirstOrDefault();
return Codes.Description;
}
and the following unit test:
[Test]
public async Task GetDescription()
{
var result = await _serivce.GetSpecialtyDescription(1);
Assert.That(result == "description");
}
and I setup my test like this:
public ServicesTests()
{
_dbContext = new Mock<LocalContext>();
var CodeList = new List<Codes>() { new Codes() { Id = 1, Description = "description" } };
var dbSetCodeList = MockDbSet.GetQueryableMockDbSet(CodeList);
_dbContext.Setup(x => x.Codes).Returns(dbSetCodeList.Object);
_context = new DataContext<LocalContext, LocalContext>(_dbContext.Object);
_serivce = new CodesServices(_context);
}
public static Mock<DbSet<T>> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
return dbSet;
}
Which all works fine.
Now, I want to await the function, so do this:
public async Task<string> GetDescription(int codeId)
{
var Codes = await Context.Codes.Where(x => x.Id == codeId).FirstOrDefaultAsync();
return Codes.Description;
}
but this breaks my unit test - even though the code still works.
I have read plenty of articles and posts explaining that this does not work as the async methods cannot be mocked (I think?), but I have not come across anything which shows me how to resolve this.
I would be disappointing if I was forced to choose between awaiting my code or having a unit test, and feel sure there must be a way around this?

I am not happy with the result, but this is the best I can do so far:
public async Task<string> GetDescription(int codeId)
{
var Codes = await GetAll();
return Codes.Description;
}
private async Task<DbSet<Codes>> GetAll()
{
return Context.Codes;
}
If anyone has a better suggestion please let me know.

Related

C# unit testing MassTransit handler with MSTest, Moq and FluentAssertions. Can't verify method called exactly once

I have this class called Handler, which is a MassTransit IConsumer:
public class Handler : IConsumer<ICommand>
{
private readonly IOrderRepository _orderRepository;
public Handler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
}
public async Task Consume(ConsumeContext<ICommand> context)
{
var command = context.Message;
var orderId = new OrderId(command.OrderId);
var order = await _orderRepository.FindOrderAsync(orderId, context.CancellationToken);
if (order is null)
{
await context.RespondAsync(CommandResponse.NotFound);
return;
}
order.Cancel();
await _orderRepository.SaveOrderAsync(order, context.CancellationToken);
await context.RespondAsync(CommandResponse.Submitted);
}
}
I have two unit tests for it. Here's the one that seems to work fine:
[TestMethod]
public async Task Consume_WithExistingOrderId_CancelsOrderAndSavesChangesAndReturnsSubmitted()
{
// Arrange
var mockConsumer = new Mock<IConsumer<ICommand>>();
var mockRepository = new Mock<IOrderRepository>();
var sut = new Handler(mockRepository.Object);
var mockCommand = new Mock<ICommand>();
var mockContext = new Mock<ConsumeContext<ICommand>>();
mockContext.Setup(x => x.Message).Returns(mockCommand.Object);
mockContext.Setup(x => x.RespondAsync(It.IsAny<CommandResponse>())).Returns(Task.CompletedTask);
var existingOrderId = new OrderId(Guid.NewGuid());
mockCommand.Setup(x => x.OrderId).Returns(existingOrderId.Value);
var order = GetTestOrder(existingOrderId);
mockRepository.Setup(x => x.FindOrderAsync(existingOrderId, It.IsAny<CancellationToken>())).ReturnsAsync(order);
// Act
await sut.Consume(mockContext.Object);
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(order, It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(x => x.RespondAsync(CommandResponse.Submitted), Times.Once());
order.IsCancelled.Should().BeTrue();
}
And here's the one that isn't doing what I expected:
[TestMethod()]
public async Task Consume_WithNonExistantOrderId_ReturnsNotFoundResponseAndDoesNotSave()
{
// Arrange
var mockRepository = new Mock<IOrderRepository>();
var sut = new Handler(mockRepository.Object);
var mockCommand = new Mock<ICommand>();
var mockContext = new Mock<ConsumeContext<ICommand>>();
mockContext.Setup(x => x.Message).Returns(mockCommand.Object);
mockContext.Setup(x => x.RespondAsync(It.IsAny<CommandResponse>())).Returns(Task.CompletedTask);
var nonExistantOrderId = new OrderId(Guid.NewGuid());
mockCommand.Setup(x => x.OrderId).Returns(nonExistantOrderId.Value);
mockRepository.Setup(x => x.FindOrderAsync(nonExistantOrderId, It.IsAny<CancellationToken>())).ReturnsAsync((Order?)null);
// Act
await sut.Consume(mockContext.Object);
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
mockContext.Verify(x => x.RespondAsync(CommandResponse.NotFound), Times.Once());
}
Both unit tests require that the Handler calls the RespondAsync method of the MassTransit context exactly once. However, the second unit test doesn't pass, saying that the method was never called. I don't see why it was never called. When I debug into the method it appears to show the method is called.
I can't tell if my test is wrong or if my system under test is wrong. Can anybody see the problem please?
(Also, if anybody can see how to make my code more testable and my unit tests shorter and simpler that would also be appreciated.)
The problem is with the nonExistantOrderId and using that for the match in expectation.
mockRepository
.Setup(x => x.FindOrderAsync(nonExistantOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
the mock expects to get that specific instance when the subject is being exercised but the subject initialized its own instance which causes the mock to not invoke the async call and exit the subject before that target line can be invoked.
This is why
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
supposedly passed verification, and
mockContext.Verify(x => x.RespondAsync(CommandResponse.NotFound), Times.Once());
failed since the subject code exited before reaching both members that are the targets of your verification.
Loosen the match using It.IsAny<OrderId>()
mockRepository
.Setup(x => x.FindOrderAsync(It.IsAny<OrderId>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
so that the mocked async call can be invoked and allow the code to flow to completion.
I have accepted Nkosi's answer because it solved the problem stated in the question. Thank you, Nkosi.
However, the resources that were provided by the MassTransit boss-man, #Chris Patterson, were helpful in making a better unit test altogether. That's why I'm posting this alternative answer. This answer rewrites the original unit test to use the MassTransit test harness, and I hope it helps somebody one day.
Chris's links have somehow disappeared from sight. However, I think he posted this:
https://www.youtube.com/watch?v=Cx-Mc0DCpfE&t=545s
And this:
https://masstransit-project.com/usage/testing.html
The rewritten method:
public interface ICommand
{
Guid OrderId { get; }
}
public record Command : ICommand
{
public Command(Guid orderId)
{
OrderId = orderId;
}
public Guid OrderId { get; }
}
public class Handler : IConsumer<ICommand>
{
private readonly IOrderRepository _orderRepository;
public Handler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository ?? throw new ArgumentNullException(nameof(orderRepository));
}
public async Task Consume(ConsumeContext<ICommand> context)
{
var command = context.Message;
OrderId orderId = new OrderId(command.OrderId);
var order = await _orderRepository.FindOrderAsync(orderId, context.CancellationToken);
if (order is null)
{
await context.RespondAsync(CommandResponse.NotFound);
return;
}
order.Cancel();
await _orderRepository.SaveOrderAsync(order, context.CancellationToken);
await context.RespondAsync(CommandResponse.Submitted);
}
}
The rewritten unit test class:
[TestClass()]
public class Handler_Tests
{
private static OrderId ExistingOrderId = new OrderId("d94108e4-1121-401a-a6ef-c7736054041d");
private static OrderId NonExistentOrderId = new OrderId("daaa72a0-8f8c-4a9b-b4ad-ebefbb6b5aa2");
private static CustomerId ExistingCustomerId = new CustomerId("5fbf40d8-c064-4821-8948-a520863e6242");
[TestMethod()]
public async Task Consume_WithExistingOrderId_CancelsOrderAndSavesChangesAndReturnsSubmitted2()
{
// Arrange
var harness = new InMemoryTestHarness();
var mockRepository = GetMockOrderRepository();
var sut = harness.Consumer(() =>
{
return new Handler(mockRepository.Object);
});
await harness.Start();
try
{
var requestClient = await harness.ConnectRequestClient<ICommand>();
var command = new Command(ExistingOrderId.Value);
// Act
var response = await requestClient.GetResponse<CommandResponse>(command, It.IsAny<CancellationToken>());
// Assert
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Once());
response.Message.Result.Should().Be(CommandResponse.Results.Submitted);
}
finally
{
await harness.Stop();
}
}
[TestMethod()]
public async Task Consume_WithNonExistentOrderId_ReturnsNotFoundResponseAndDoesNotSave()
{
// Arrange
var harness = new InMemoryTestHarness();
var mockRepository = GetMockOrderRepository();
var sut = harness.Consumer(() =>
{
return new Handler(mockRepository.Object);
});
await harness.Start();
try
{
var requestClient = await harness.ConnectRequestClient<ICommand>();
var command = new Command(NonExistentOrderId.Value);
var response = await requestClient.GetResponse<CommandResponse>(command, It.IsAny<CancellationToken>());
mockRepository.Verify(x => x.SaveOrderAsync(It.IsAny<Order>(), It.IsAny<CancellationToken>()), Times.Never());
response.Message.Result.Should().Be(CommandResponse.Results.NotFound);
}
finally
{
await harness.Stop();
}
}
private static Order GetTestOrder(OrderId orderId)
{
var orderItem = GetTestOrderItem();
return Order.Place(orderId, ExistingCustomerId, new[] { orderItem });
}
private static OrderItem GetTestOrderItem()
{
var productId = new ProductId(Guid.NewGuid());
var price = new Price(1, PurchaseCurrency.Usd);
var quantity = new Quantity(1, UnitOfMeasure.Each);
return new OrderItem(productId, quantity, price);
}
private static Mock<IOrderRepository> GetMockOrderRepository()
{
var mockRepository = new Mock<IOrderRepository>();
mockRepository.Setup(x => x.FindOrderAsync(It.IsAny<OrderId>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException(
$"This mock is set up to work with {nameof(ExistingOrderId)} and {nameof(NonExistentOrderId)}."));
mockRepository.Setup(x => x.FindOrderAsync(ExistingOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync(GetTestOrder(ExistingOrderId));
mockRepository.Setup(x => x.FindOrderAsync(NonExistentOrderId, It.IsAny<CancellationToken>()))
.ReturnsAsync((Order?)null);
return mockRepository;
}
}

How can I write unit Tests in .NET for web api

My controller for the delete method :
[HttpDelete("{toDoListId}")]
public async Task<ActionResult> DeleteToDoList(int toDoListId)
{
var toDoListEntity = await _toDoListRepository.GetSpecificTodoAsync(toDoListId);
if (toDoListEntity == null)
{
return NotFound();
}
_toDoListRepository.DeleteToDoList(toDoListEntity);
await _toDoListRepository.SaveChangesAsync();
return NoContent();
}
My repository :
public async Task<ToDoList?> GetSpecificTodoAsync(int taskId)
{
return await _context.ToDoLists.Where(c => c.Id == taskId).FirstOrDefaultAsync();
}
public void DeleteToDoList(ToDoList toDoListDto)
{
_context.ToDoLists.Remove(toDoListDto);
}
My testcase for checking if the item got deleted and if it returns no content after being deleted. But both of my test cases are failing. Any help on how to write test cases for the delete part, I would be really grateful. I am also trying to test other methods but I am unfortunately stuck here. Please kindly help me
public class UnitTest1
{
private readonly Mock<IToDoListRepository> repositoryStub = new ();
private readonly Mock<IMapper> mapper = new Mock<IMapper>();
private readonly Random Rand = new();
private ToDoList GenerateRandomItem()
{
return new()
{
Id = Rand.Next(),
Description= Guid.NewGuid().ToString(),
Title = Guid.NewGuid().ToString(),
StartDate = DateTime.Now,
EndDate = DateTime.Now,
Done = false
};
}
[Fact]
public void Delete_removesEntry()
{
//arrange
var existingItem = GenerateRandomItem();
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
var itemID = existingItem.Id;
//act
controller.DeleteToDoList(itemID);
//assert
Assert.Null(repositoryStub.Object.GetSpecificTodoAsync(itemID));
}
[Fact]
public async Task DeleteItemAsync_WithExistingItem_ReturnNoContent()
{
//Arrange
var existingItem = GenerateRandomItem();
repositoryStub.Setup(repo => repo.GetSpecificTodoAsync(existingItem.Id)).ReturnsAsync((existingItem));
var itemID = existingItem.Id;
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//Act
var result = await controller.DeleteToDoList(itemID);
//assert
result.Should().BeOfType<NoContentResult>();
}
Both test cases are failing because the mock has not been setup to behave as expected for each test case.
There is also a potential race condition in the shown tests since they are sharing the same mock instance. This will cause issues when setting up the mock as one test case could potentially override the setup of another case.
Update the tests so that they are isolated from each other.
In the first test, the expected behavior can be verified by checking the mock to see if the expected member was invoked.
[Fact]
public async Task DeleteToDoList_Should_RemoveEntry() {
//arrange
ToDoList existingItem = GenerateRandomItem();
var itemID = existingItem.Id;
Mock<IToDoListRepository> repositoryStub = new ();
//Setup expected behavior of mock
repositoryStub
.Setup(_ => _.GetSpecificTodoAsync(itemID))
.ReturnsAsync(existingItem);
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//act
await controller.DeleteToDoList(itemID);
//assert
repositoryStub.Verify(_ => _.DeleteToDoList(existingItem));
}
In the other test, the mock needs be setup to make sure the subject executes to completion.
[Fact]
public async Task DeleteToDoList_WithExistingItem_Should_ReturnNoContent() {
//Arrange
ToDoList existingItem = GenerateRandomItem();
var itemID = existingItem.Id;
Mock<IToDoListRepository> repositoryStub = new ();
//Setup expected behavior of mock
repositoryStub
.Setup(_ => _.GetSpecificTodoAsync(itemID))
.ReturnsAsync(existingItem);
repositoryStub.Setup(_ => _.SaveChangesAsync()).ReturnsAsynt(true);
var controller = new ToDoController(repositoryStub.Object, mapper.Object);
//Act
ActionResult result = await controller.DeleteToDoList(itemID);
//assert
result.Should().BeOfType<NoContentResult>();
}

moq method call not being verified

I have a test method that is failing on sso.verify notice the CheckUsername method has two await calls in a async method ? but because of that the sso verify never returns and thus is not verified. But the code is called. What would be the proper way to test this ?
public void Setup()
{
nav = new Mock<INavService>();
sso = new Mock<ISSOApiService>();
_vm_Successs = new ForgotPasswordViewModel(nav.Object, sso.Object);
sso.Setup(x => x.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>())).ReturnsAsync(new StandardResponse() { ErrorCode = null }).Verifiable();
nav.Setup(x => x.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>())).Verifiable();
}
[Test]
public void CheckUserName_Success()
{
_vm_Successs.UserName = "Timmy";
var response = _vm_Successs.CheckUsername();
sso.Verify(e => e.SendEmailCodeRequest(It.IsAny<PasswordTokenRequest>()), Times.Once);
nav.Verify(mock => mock.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>()), Times.Once);
}
This is the checkusername method
public async Task CheckUsername()
{
PasswordTokenRequest r = new PasswordTokenRequest();
await SSOAPIService.SendEmailCodeRequestAsync(r);
await NavService.NavigateTo<ForgotPasswordEnterCodeModel, string>(UserName);
}
you should await test method therefore you need to make your test 'async Task' type
also need to setup SendEmailCodeRequestAsync with ReturnsAsync
[Test]
public async Task ShouldDeleteAzureBlobById()
{
sso.Setup(x => x.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>()))
.ReturnsAsync(new StandardResponse() { ErrorCode = null })
.Verifiable();
_vm_Successs.UserName = "Timmy";
var response = await _vm_Successs.CheckUsername();
sso.Verify(e => e.SendEmailCodeRequestAsync(It.IsAny<PasswordTokenRequest>()), Times.Once);
nav.Verify(mock => mock.NavigateTo<ForgotPasswordEnterCodeModel, string>(It.IsAny<string>()), Times.Once);
}

EF Core Unit Test Mocking

I am attempting to mock out some of EF Core's functions.
Originally I was getting the following error
The provider for the source IQueryable doesn't implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.
After looking at this question it almost seems to work but i'm now getting the following:
System.ArgumentNullException : Value cannot be null.
Parameter name: source
at Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T](T value, String parameterName)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable1 source, Expression1 predicate, CancellationToken cancellationToken)
at Ombi.Core.Rule.Rules.Search.RadarrCacheRule.d__2.MoveNext() in C:\Users\Jamie.Rees\Source\Repos\PlexRequests.Net\src\Ombi.Core\Rule\Rules\Search\RadarrCacheRule.cs:line 25
Here is the test code:
[Test]
public async Task Should_ReturnApproved_WhenMovieIsInRadarr()
{
var list = new List<RadarrCache>(){new RadarrCache
{
TheMovieDbId = 123
}}.AsQueryable();
var radarrMock = new Mock<DbSet<RadarrCache>>();
radarrMock.As<IAsyncEnumerable<RadarrCache>>()
.Setup(m => m.GetEnumerator())
.Returns(new TestAsyncEnumerator<RadarrCache>(list.GetEnumerator()));
radarrMock.As<IQueryable<RadarrCache>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<RadarrCache>(list.Provider));
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.Expression).Returns(list.Expression);
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.ElementType).Returns(list.ElementType);
radarrMock.As<IQueryable<RadarrCache>>().Setup(m => m.GetEnumerator()).Returns(() => list.GetEnumerator());
ContextMock.Setup(c => c.Set<RadarrCache>()).Returns(radarrMock.Object);
var request = new SearchMovieViewModel { Id = 123 };
var result =await Rule.Execute(request);
Assert.True(result.Success);
Assert.True(request.Approved);
}
This is the class under test:
public class RadarrCacheRule : BaseSearchRule, IRules<SearchViewModel>
{
public RadarrCacheRule(IOmbiContext ctx)
{
_ctx = ctx;
}
private readonly IOmbiContext _ctx;
public async Task<RuleResult> Execute(SearchViewModel obj)
{
if (obj.Type == RequestType.Movie)
{
// Check if it's in Radarr
var result = await _ctx.RadarrCache.FirstOrDefaultAsync(x => x.TheMovieDbId == obj.Id);
if (result != null)
{
obj.Approved =
true; // It's in radarr so it's approved... Maybe have a new property called "Processing" or something?
}
}
return Success();
}
}
Any idea how I am suppose to do this?

Moqing UnitOfWork with lambda Expression

I have something like this to test:
public void example(ModelView modelView)
{
//...
var statustId= 1;
var requests = _unitOfWork.RequestRepository.Get(m => m.RequestStatusId == statusId);
var requests = _unitOfWork.RequestRepository.Get(m => m.RequestTypeId == modelView.RequestTypeId);
var oldRequests = _unitOfWork.RequestRepository.Get(m => m.Created == DateTime.Now.AddDays(-7));
//...
}
How do i mock all the "GET" with different data?
This is what i got so far: not sure what the expression should look like since it is using a variables from the function.
[Test]
public void Test()
{
//Arrange
_requestGenericRepo.Setup(m => m.Get(
It.Is<Expression<Func<Request, bool>>>(e => e.Equals(First GET - using local variable);
_requestGenericRepo.Setup(m => m.Get(
It.Is<Expression<Func<Request, bool>>>(e => e.Equals(Second GET - using variable from modelView));
_requestGenericRepo.Setup(m => m.Get(
It.Is<Expression<Func<Request, bool>>>(e => e.Equals(Third GET));
//Act
var response = _controller.Example( new ModelView
{
RequestTypeId= 1
});
//Assert
...
}
-------------------EDIT: -------------------------------------------
It would be nice to know how to do in the above way but I can change all Get( To Get().Where like this:
public void example(ModelView modelView)
{
//...
var statustId= 1;
var requests = _unitOfWork.RequestRepository.Get().Where(m => m.RequestStatusId == statusId);
var requests = _unitOfWork.RequestRepository.Get().Where(m => m.RequestTypeId == modelView.RequestTypeId);
var oldRequests = _unitOfWork.RequestRepository.Get().Where(m => m.Created == DateTime.Now.AddDays(-7));
//...
}
[Test]
public void Test()
{
//Arrange
_requestGenericRepo.Setup(m => m.Get(
It.IsAny<Expression<Func<Request, bool>>>());
//Act
var response = _controller.Example( new ModelView
{
RequestTypeId= 1
});
//Assert
...
}
What is the return value of the RequestRepository.Get method? You can try to use SetupSequence and choose path you want to test. Something like:
_requestGenericRepo.SetupSequence(m => m.Get(It.IsAny<Expression<Func<Request, bool>>>())
.Returns(/*First call*/)
.Returns(/*Second call*/)
.Returns(/*Third call*/);

Categories