Minimal APIs in .NET 6 but where are the Unit Tests?
Minimal APIs in .NET 6 is great. But where are the Unit Tests?! Often testing is missed or forgotten because it's perceived as difficult or complex.
- Exploring a minimal Web API with ASP.NET Core 6
- A .NET 6 Minimal API Todo example Playground
- Exploring a minimal WebAPI with ASP.NET Core
- Easier functional and integration testing of ASP.NET Core applications
- Automatic Unit Testing in .NET Core plus Code Coverage in Visual Studio Code
But it's super fun and very easy! Once tests are easy to write, WRITE A LOT OF THEM.
Here's a simple Unit Test of a Web API:
[Fact]
public async Task GetTodos()
{
await using var application = new TodoApplication();
var client = application.CreateClient();
var todos = await client.GetFromJsonAsync<List<Todo>>("/todos");
Assert.Empty(todos);
}
Look how nice that is. Client and Server (Application) are right there, and the HTTP GET is just a function call (as this is a Unit Test, not an integration test that covers end-to-end full stack).
Here's the TodoApplication application factory that creates a Host with a mocked out in memory version of a SQLite database.
class TodoApplication : WebApplicationFactory<Todo>
{
protected override IHost CreateHost(IHostBuilder builder)
{
var root = new InMemoryDatabaseRoot();
builder.ConfigureServices(services =>
{
services.AddScoped(sp =>
{
// Replace SQLite with the in memory provider for tests
return new DbContextOptionsBuilder<TodoDbContext>()
.UseInMemoryDatabase("Tests", root)
.UseApplicationServiceProvider(sp)
.Options;
});
});
return base.CreateHost(builder);
}
}
Nice and clean. You're talking directly to the API, testing just the Unit of Work. No need for HTTP, you're just calling a clean method on the existing API, directly.
That's a simple example, just getting Todos. How would we test making one (POSTing to our Todo application as a Minimal .NET 6 API?)
[Fact]
public async Task PostTodos()
{
await using var application = new TodoApplication();
var client = application.CreateClient();
var response = await client.PostAsJsonAsync("/todos", new Todo { Title = "I want to do this thing tomorrow" });
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var todos = await client.GetFromJsonAsync<List<Todo>>("/todos");
Assert.Single(todos);
Assert.Equal("I want to do this thing tomorrow", todos[0].Title);
Assert.False(todos[0].IsComplete);
}
You could abstract the setup away if you wanted to and start with an Server/App and Client ready to go, but it's just two lines.
Here we are asserting that it returned an HTTP 200 - even though the HTTP networking stack isn't involved we are still able to test intent. Then we confirm that we created a Todo and could successfully retrieve it from the (in-memory) database.
Pretty slick!
Sponsor: YugabyteDB is a distributed SQL database designed for resilience and scale. It is 100% open source, PostgreSQL-compatible, enterprise-grade, and runs across all clouds. Sign up and get a free t-shirt.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Comments are closed.