Scott Hanselman

Minimal APIs in .NET 6 but where are the Unit Tests?

September 09, 2021 Comment on this post [0] Posted in DotNetCore | Open Source | Web Services
Sponsored By

imageMinimal 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.

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.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.