C
C#3mo ago
mellowzippy

Unit Testing ASP.NET async api service

Okay. I am trying to make a unit test for my Customer Service. I tried starting with the Read operation which looks like this in my service:
public async Task<Customer> Read(int customerId)
{
return await _context.Customers
.FirstOrDefaultAsync(c => c.CustomerId == customerId);
}
public async Task<Customer> Read(int customerId)
{
return await _context.Customers
.FirstOrDefaultAsync(c => c.CustomerId == customerId);
}
I tried using this as a guide: https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking#testing-query-scenarios
Testing with a mocking framework - EF6
Testing with a mocking framework in Entity Framework 6
17 Replies
mellowzippy
mellowzippyOP3mo ago
I tried implementing it myself like this:
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(5)]
[InlineData(6)]
public async Task CustomerRead(int customerId)
{
// Arrange
var data = new List<Customer>
{
new() { CustomerId = 1, Email = "[email protected]", FirstName = "Willy", LastName = "the Nilly" },
new() { CustomerId = 2, Email = "[email protected]", FirstName = "Billy", LastName = "the Hilly" },
new() { CustomerId = 3, Email = "[email protected]", FirstName = "Silly", LastName = "the Trippy" },
new() { CustomerId = 4, Email = "[email protected]", FirstName = "Jeffrey", LastName = "Geoff" },
new() { CustomerId = 5, Email = "[email protected]", FirstName = "John", LastName = "Smith" }
}.AsQueryable();

var mockSet = new Mock<DbSet<Customer>>();

mockSet.As<IQueryable<Customer>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Customer>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Customer>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Customer>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

var mockContext = new Mock<TheatreDbContext>(new DbContextOptions<TheatreDbContext>());
mockContext.Setup(c => c.Customers).Returns(mockSet.Object);

var service = new CustomerService(mockContext.Object);

// Act
var foundCustomer = await service.Read(customerId);

// Assert
if (customerId == 0 || customerId == 6)
{
Assert.Null(foundCustomer);
}
else
{
Assert.NotNull(foundCustomer);
Assert.IsType<Customer>(foundCustomer);
Assert.Equal(customerId, foundCustomer.CustomerId);
}
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(5)]
[InlineData(6)]
public async Task CustomerRead(int customerId)
{
// Arrange
var data = new List<Customer>
{
new() { CustomerId = 1, Email = "[email protected]", FirstName = "Willy", LastName = "the Nilly" },
new() { CustomerId = 2, Email = "[email protected]", FirstName = "Billy", LastName = "the Hilly" },
new() { CustomerId = 3, Email = "[email protected]", FirstName = "Silly", LastName = "the Trippy" },
new() { CustomerId = 4, Email = "[email protected]", FirstName = "Jeffrey", LastName = "Geoff" },
new() { CustomerId = 5, Email = "[email protected]", FirstName = "John", LastName = "Smith" }
}.AsQueryable();

var mockSet = new Mock<DbSet<Customer>>();

mockSet.As<IQueryable<Customer>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Customer>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Customer>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Customer>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

var mockContext = new Mock<TheatreDbContext>(new DbContextOptions<TheatreDbContext>());
mockContext.Setup(c => c.Customers).Returns(mockSet.Object);

var service = new CustomerService(mockContext.Object);

// Act
var foundCustomer = await service.Read(customerId);

// Assert
if (customerId == 0 || customerId == 6)
{
Assert.Null(foundCustomer);
}
else
{
Assert.NotNull(foundCustomer);
Assert.IsType<Customer>(foundCustomer);
Assert.Equal(customerId, foundCustomer.CustomerId);
}
}
But when I run dotnet test it tells me: The provider for the source 'IQueryable' doesn't implement 'IAsyncQueryProvider'. Only providers that implement 'IAsyncQueryProvider' can be used for Entity Framework asynchronous operations.. And because this is me writing my first test I have absolutely no clue what this means. And I don't really know where to start to debug it. I tried adding the "TestDbAsyncQueryProvider" code they provided but it changed absolutely nothing in the error so it didn't help me to fix it. $paste
MODiX
MODiX3mo ago
If your code is too long, you can post to https://paste.mod.gg/, save, and copy the link into chat for others to see your shared code!
mellowzippy
mellowzippyOP3mo ago
BlazeBin - byjidngksegg
A tool for sharing your source code with the world!
mellowzippy
mellowzippyOP3mo ago
I was able to test it if I made my service (and the test) synchronous, the error only came when I tried to make it async.
mellowzippy
mellowzippyOP3mo ago
If one looks at this code and thinks that it is quite the mess, I would appreciate perhaps good videos or websites on how to do this correctly.
Sossenbinder
Sossenbinder3mo ago
I'm on my phone and can't comment on your mocking setup, but as a general guidance I'd probably not test ef core logic since it's not something maintained by yourself. If you have complex enough queries to warrant a test, I'd always favor an integration test with test containers of the database you'll actually run with in the end Since you get to test the sql translation which will actually take place later on If you mock things or use an in memory ef core db, then you're kinda ripping out 80% of the code and your test has little representation of what's going on
mellowzippy
mellowzippyOP3mo ago
What are integration tests used for? Because (for now) it is only an API. Not connected to any other program so it's not "integrated" with anything right?
Sossenbinder
Sossenbinder3mo ago
If you use ef, you'd probably be integrating with a database It qualifies as another system you integrate with
mellowzippy
mellowzippyOP3mo ago
Ooo okay Would you happen to have any links that show how to do proper integration testing?
Sossenbinder
Sossenbinder3mo ago
There's a good msdn article for integration tests with asp net core https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-8.0. This might get you the general idea 👍 If you ask me, I'd definitely recommend reading on testcontainers as well if you're doing integration testing in any form, but I feel like you're just getting started with testing, so taking it step by step is a good idea 👍
Integration tests in ASP.NET Core
Learn how integration tests ensure that an app's components function correctly at the infrastructure level, including the database, file system, and network.
mellowzippy
mellowzippyOP3mo ago
Thank you @Sossenbinder Do you think I should drop the unit tests than? then* Or perhaps use them to test something differently?
Sossenbinder
Sossenbinder3mo ago
This wouldn't be something I'd unit test, no. I mainly unit test complex logic and try to keep the dependencies to external systems pretty clean within, so I don't have to rely on mocking and can just pass input and output. When it comes to database interaction which warrants testing, I usually reach out for integration tests
mellowzippy
mellowzippyOP3mo ago
Is there anything I should unit test in my API? Don't know what would be complex logic. I feel like my API is pretty simple just CRUD mostly
Sossenbinder
Sossenbinder3mo ago
That depends on your code I assume. If it's a pretty basic crud api without much logic, it might not be something which contains a lot of unit testable code
Sossenbinder
Sossenbinder3mo ago
https://enterprisecraftsmanship.com/posts/when-to-mock/ Here's another great link. Generally recommend vladimir khorikov for testing theory, his book is great
Enterprise Craftsmanship
When to Mock
The use of mocks in unit testing is a controversial topic (maybe less so now than several years ago). I remember how, throughout my programming career, I went from mocking almost every dependency, to the "no-mocks" policy, and then to "only mock external dependencies". None of this practices are good enough. In this article, I’ll show you whi...
mellowzippy
mellowzippyOP3mo ago
Okayyy I will try to use this, thanks a lot
Want results from more Discord servers?
Add your server