How to test ASP.NET Core Minimal APIs
Time to read: 8 minutes
How do you test that your ASP.NET Core Minimal API behaves as expected? Do you need to deploy your application? Can you write tests with frameworks like xUnit, NUnit, or MSTest?
In this post, you will learn the basics of testing ASP.NET Core Minimal APIs. You’ll get started with testing a “hello world” endpoint, and then test a more complex API that returns JSON data. You’ll finish with customizing the ASP.NET Core service collection, so you can customize services for your unit tests and integration tests.
By the end of this post, you will have a good understanding of how to make sure your ASP.NET Core Minimal APIs behave as expected and can be deployed to production, even on Fridays!
Prerequisites
- An OS that supports .NET (Windows/macOS/Linux)
- A .NET IDE (JetBrains Rider, Visual Studio, VS Code with C# plugin, or any editor of your choice)
- .NET 6.0 SDK or later
You can find the source code for this tutorial on GitHub. Use it as a reference if you run into any issues.
Create a test project
To get started, you will need to create a solution with two projects: an ASP.NET Core Minimal API that will contain the application, and a unit test project that will contain the tests. In this blog post, you will use xUnit as the testing framework.
You can create this solution in your favorite .NET IDE, or using the .NET CLI. In the command line or terminal window, navigate to the folder you want your project to be created in, and run the following commands:
You now have a MyMinimalApi.sln file, and two projects (MyMinimalApi.csproj for the ASP.NET Core Minimal API, and MyMinimalApi.Tests.csproj for the unit tests) with some template code. The test project also has a project reference to the Minimal API project.
To run the Minimal API application, you can use the .NET CLI and specify the project to run:
The tests can be run using the following .NET CLI command:
There’s not a lot of useful code in these projects yet. The Minimal API project contains a Program.cs file with an endpoint that returns the string “Hello World!”:
The test project (MyMinimalApi.Tests.csproj) contains a template unit test file UnitTest1.cs that you will replace later in this article.
Update the test project
Before you can start testing your Minimal API, you will need to make some updates to the test project. The unit tests need to be able to use the ASP.NET Core framework, so you’ll have to bring that in somehow. The easiest way to do this is by adding a reference to the Microsoft.AspNetCore.Mvc.Testing
package. This package also comes with several helper classes that are invaluable when writing unit tests later on.
Add this package using your favorite IDE, or use the .NET CLI:
The MyMinimalApi.Tests.csproj file now looks like this:
You can now start writing unit tests for your Minimal API.
“Hello World” and the ASP.NET Core test server
In the Minimal API project, Program.cs already defines a “Hello World!” endpoint. You will test this endpoint first. Before you can do this, you will need to add the following public partial class definition at the bottom of Program.cs:
The reason why you need this partial class definition, is that by default the Program.cs file is compiled into a private class Program
, which can not be accessed by other projects. By adding this public partial class, the test project will get access to Program
and lets you write tests against it.
In the MyMinimalApi.Tests project, rename the UnitTest1.cs file to HelloWorldTests.cs and update the code:
The TestRootEndpoint()
test will have to do a couple of things:
- Start the ASP.NET Core Minimal API
- Create an HTTP client for to connect to the application
- Send an HTTP request to the
/
endpoint - Verify the response
Earlier in this post, you have added a reference to the Microsoft.AspNetCore.Mvc.Testing
package. This package contains the WebApplicationFactory<T>
, which is an important building block for testing ASP.NET Core applications.
The WebApplicationFactory<T>
class creates an in-memory application that you can test. It handles bootstrapping of your application, and provides an HttpClient
that you can use to make requests.
Update the code in the TestRootEndpoint()
method:
The code uses WebApplicationFactory<Program>
. Here’s the reason you had to add that public partial class! You can use other public classes from the Minimal API project as well, but I personally prefer Program
as it’s there in every project.
You can run this test using the .NET CLI, and look at the results:
The test you created has just started your Minimal API application using the WebApplicationFactory<Program>
, and uses an HttpClient
that was returned by application.CreateClient()
. Using this client, the test makes an HTTP GET request to the /
endpoint. In this example, you used the GetStringAsync("/")
method to do this. The test then asserts the response matches what is expected.
Congratulations, you have just created your first test for an ASP.NET Core Minimal API!
Update the Minimal API project
Let’s spice things up a little! In most APIs, endpoints will work with JSON payloads in requests and responses. An API endpoint may return different results depending on the request that is being made. It may return a 200 OK
status code on success, and a 400 Bad Request
status code with more details in the response body when the request was not valid.
In this section, you will add such an endpoint to the Minimal API. This endpoint will also perform validation of the request, using the MiniValidation package.
Add this package using your favorite IDE, or use the .NET CLI:
When that is installed, add a Person
class to your Minimal API. This class will be used as a request payload later on.
Note that the Person
class adds validation attributes from the System.ComponentModel.DataAnnotations
namespace. Add using System.ComponentModel.DataAnnotations;
to the top of your Program.cs file to include the namespace . The MiniValidation
packages you added earlier can process these attributes and validate the request is well-formed.
The Minimal API will also need to be able to store the Person
in a data store. While modeling this data store is not in the scope of this article, you can define an IPeopleService
interface to interact with the data store, and a PeopleService
class that implements this interface:
It’s now time to register the IPeopleService
with the ASP.NET Core service collection, so your API endpoint can make use of it. Add it as a scoped service to make sure a new instance of PeopleService
is created each time a request comes in:
You are doing great! As a final step in this section, you will implement the actual API endpoint in your Minimal API. This endpoint will listen for POST
requests on /people
, and accept a Person
object in the request body. After the endpoint validates the incoming request, the API either uses the IPeopleService
to store the object in the database, or returns a validation result.
Add using MiniValidation;
to your using statements at the top of Program.cs class so you can use the MiniValidator
class.
Just to make sure, here’s what your Program.cs should now look like:
If you want to, you can run the Minimal API and test the /people
endpoint from your terminal.
First, start your Minimal API using dotnet run --project MyMinimalApi
and look for the localhost URL in the output.
If you have the curl
command available in your terminal, run:
Or if you're using PowerShell, run:
Replace the https://localhost:7230
with the localhost URL that the dotnet run
command printed to the console.
The response should be a 400 Bad request
, since the LastName
and Email
properties are required:
After you confirm the endpoint works, you will convert this request into a test!
Test different payloads and HTTP methods
Your Minimal API now has a /people
endpoint. It has two possible response types: a 200 OK
that returns a string value, and a 400 Bad Request
that returns problem details as a JSON payload.
In the MyMinimalApi.Tests project, add a PeopleTests.cs file that contains the following code:
The PeopleTests
class now contains 2 test methods that you will need to implement:
CreatePerson()
to test the200 OK
scenarioCreatePersonValidatesObject()
to test the400 Bad Request
scenario
You will start with the CreatePerson()
test method. The test will again make use of the WebApplicationFactory<Program>
to create an in-memory HTTP client that you can use to validate the API.
Next, you will use the client
to send a JSON payload to the /people
endpoint. You can use the PostAsJsonAsync()
method to send a JSON payload to the Minimal API under test. Finally, you can use the xUnit Assert
class to validate the response status code and the response content.
Update the CreatePerson()
test like below:
You can run this test using the .NET CLI, and confirm your Minimal API works as expected.
The CreatePersonValidatesObject()
test is next. Like in the CreatePerson()
test method, you will begin with creating a request to the in-memory Minimal API. Only this time, you will send an empty Person
object.
Since all of its properties will be null
or empty, the test should get back a 400 Bad Request
. You can assert this is indeed the case. What’s more, you can also use the result.Content.ReadFromJsonAsync<>()
method to deserialize the validation problems, and verify they are as expected.
Update the CreatePersonValidatesObject()
test like below:
I will leave the validation of the other properties as an exercise for you.
Again, try running this test using the .NET CLI, and confirm your Minimal API works as expected.
Well done! You have now written tests that validate JSON payloads accepted and returned by your Minimal API!
Customizing the service collection
There’s one more thing… The Minimal API you created contains a PeopleService
that, in a more real-life project, could need a database connection. This could be okay for some tests, and unnecessary for others.
The tests that you have written so far all have been validating the responses of the Minimal API. There’s no real need for the “real” implementation of IPeopleService
, so let’s see how you can swap it out with a test implementation!
In the MyMinimalApi.Tests project, create a new file TestPeopleService.cs with the following code:
The TestPeopleService
class implements IPeopleService
just like the real implementation does, but the Create
method returns a simple string
value.
Next, you will update the test methods to configure the WebApplicationFactory<Program>
with a service override for IPeopleService
, wiring it to TestPeopleService
instead. You can do this in a number of ways: using the WithWebHostBuilder()
and ConfigureServices()
methods, or by implementing a custom WebApplicationFactory<T>
. In this tutorial, you will use the first approach to change the IPeopleService
to be a TestPeopleService
.
Update the CreatePerson
test with the following code:
To use services.AddScoped
, add using Microsoft.Extensions.DependencyInjection;
to your using statements at the top of the file.
Note that in the code sample, the final Assert.Equal
is now testing for the string
that is returned by TestPeopleService
.
Depending on how many customizations you want to make to your Minimal API under test, you can move the WithWebHostBuilder()
and ConfigureServices()
methods out, and override the WebApplicationFactory<T>
class. This has the advantage of having one place where you customize the service collection.
For example, you can create a TestingApplication
class and override the CreateHost
method to customize the service collection:
You can use it in tests by replacing new WebApplicationFactory<Program>
with new TestingApplication()
:
If you want to start customizing the Minimal API during tests, make sure to explore the various methods of WebApplicationFactory<T>
that you can override to configure your application for the tests you are writing.
Conclusion
That’s it! You just built several tests for an ASP.NET Core Minimal API, and validated it behaves as expected. You started out with testing a basic endpoint that returned a string, and then saw how to work with different HTTP methods and payloads on the request and response. You even customized the ASP.NET Core service collection with custom services for your tests.
Whether you are writing unit tests, integration tests or both, you should now have a good understanding of how to go about using the test server and customizing the service collection for many scenarios.
If you’re hungry for more, check out the Microsoft docs on integration testing. Or continue reading on the Twilio blog and learn about How to use Twilio SMS and Voice with a .NET 6 Minimal API and Unit Testing Twilio Programmable SMS in .NET Core.
Maarten Balliauw loves building web and cloud apps. His main interests are in .NET web technologies and application performance. He is Developer Advocate at JetBrains, creator of SpeakerTravel. Maarten is a frequent speaker at various conferences, and blogs at https://blog.maartenballiauw.be. He can be reached on Twitter.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.