Special Offer: My C#/.NET Bootcamp Course is out now. Get 10% OFF using the code FRIENDS10.

Building backend APIs has become even simpler with ASP.NET Core 6. But what about testing? Learn how to create integration tests for ASP.NET Core 6 WebAPI applications.

I’m a software engineer with more than ten years of experience with the .NET platform. On this channel, you learn all about .NET development.

The WebAPI project

We want to test the most simplistic API that you’ll ever see. It’s only seven lines of code and contains two routes. On the default route, the API returns “Hello World”, and for the /sum route, you can provide two arguments, and the API will calculate and return their sum.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");
app.MapGet("/sum", (int? n1, int? n2) => n1 + n2);

app.Run();

If you want to learn more about this simple style to build WebAPIs in .NET 6 called MinimalAPIs, check out the Introduction to Minimal APIs article or video.

Creating the Test Project

Now, let’s add a test project to the solution. We open the Add a new project dialog and select the MsTest Test Project template from the list.

On the next page, we choose MinimalAPIDemo.Test as the project name. 

On the last page, we select .NET 6.0 as the target framework for the test project and click on the Create button.

After a few seconds, Visual Studio created the test project for us and added it to the solution. Let’s quickly rename the created test class to ApiTests.

Setting up the Test Project

We don’t want to develop the test from scratch. Instead, we want to use already provided data types to make it as simple as possible to write an API test. Therefore, we open the NuGet package manager and install the Microsoft.AspNetCore.Mvc.Testing package.

Next, we want to reference the project that implements the APIs from the test project to access the Program class and run an instance of the WebAPI project from within our tests.

In the solution explorer, we right-click on the dependencies folder of the test project and select the “Add project reference” menu item. In The dialog, we select the MinimalAPIDemo project that contains our APIs and leave the dialog using the OK button.

We need to do one last thing before writing the first test. Since the Program class is internal, we need to make the internal types visible to the test project. Starting with .NET 5, we can do that in the project file. Let’s open the project file of the WebAPI project in Visual Studio.

I insert a snippet that contains an InternalsVisibleTo node within an ItemGroup tag. Using the Include attribute and the AssemblyName variable, we declare that the internal types should be visible to our test project.

<ItemGroup>
  <InternalsVisibleTo Include="$(AssemblyName).Test" />
</ItemGroup>

We save the file and open the ApiTests.cs file in the test project.

Implementing the First Test

Let’s rename the method to DefaultRoute_ReturnsHelloWorldString. We want to call the default route and test if the API returns the “Hello World” string.

Now let’s start with the implementation of the test method. We create an instance of the WebApplicationFactory type. It’s a generic type, and we provide the Program class from the API project as its type argument. We add a using statement for the “Microsoft.AspNetCore.Mvc.Testing” namespace to make it compile.

var webAppFactory = new WebApplicationFactory<Program>();

The type comes from the NuGet package we installed when setting up the test project.

It’s important to understand that the WebApplicationFactory will run the APIs in the test execution context. It means that you don’t have to run the APIs to execute the tests. You can execute the tests, and the WebApplicationFactory type will start a server.

Next, we create an httpClient variable of type HttpClient using the CreateDefaultClient method of the webAppFactory variable. This call provides us with an HTTP client that we can use to send HTTP requests to the server running in the test execution.

var httpClient = webAppFactory.CreateDefaultClient();

Next, we use the GetAsync method on the httpClient type and provide an empty string to specify that we want to call the default route. The GetAsync method is (as the name intends) an asynchronous method. We use the await keyword and change the return type of the test method to Task and define it as async.

var response = await httpClient.GetAsync("");

Next, we access the response and use the ReadAsStringAsync method on the Content property of the response object to retrieve the data returned from the API.

var stringResult = await response.Content.ReadAsStringAsync();

As the last step, we use the Assert.AreEquals method to check the content of the stringResult variable against our expected “Hello World!” string.

Assert.AreEqual("Hello World!", stringResult);

Now, let’s compile and run the test. As you can see from the green checkmark, the test passes. It takes less than a second to run the test on my machine. It includes starting the web API application, creating an HTTP client, and running the test case.

If you got any value out so far, this is the perfect time to let me know with a thumbs up so that YouTube can share this video with more .NET developers.

You can find the full source code at the end of this article.

Implementing the Second Test

Now let’s add a second test method and name it Sum_Returns16For10And6. We’re going to test the second API listening on the /Sum route.

Before we implement the second test, let’s extract the test setup code into the constructor of the class so that we can reuse it for the second test case.

We create a constructor and move the first two lines from the first test method. We also change the httpClient from a local variable to a private field and use the field in the first test method.

Now, let’s implement the second test case. First, we call the HTTP client and use the GetAsync method again. This time, we provide the /Sum route with n1 equals 10 and n2 equals 6 as the request URI.

var response = await _httpClient.GetAsync("/sum?n1=10&n2=6");

Next, we read the content from the response as a string and parse it into an int variable.

var stringResult = await response.Content.ReadAsStringAsync();
var intResult = int.Parse(stringResult);

Last but not least, we write an assert statement that verifies that the result we get from the API call equals the expected result, which is 16 in this test case.

Assert.AreEqual(16, intResult);

Let’s compile and execute both tests. As you can see, we see the green checkmark again, and both tests were completed as expected.

Source Code

This is the full code completed in this article. You can also find it on GitHub.

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Net.Http;
using System.Threading.Tasks;

namespace MinimalAPIDemo.Test
{
  [TestClass]
  public class ApiTests
  {
    private HttpClient _httpClient;

    public ApiTests()
    {
      var webAppFactory = new WebApplicationFactory<Program>();
      _httpClient = webAppFactory.CreateDefaultClient();
    }

    [TestMethod]
    public async Task DefaultRoute_ReturnsHelloWorld()
    {
      var response = await _httpClient.GetAsync("");
      var stringResult = await response.Content.ReadAsStringAsync();

      Assert.AreEqual("Hello World!", stringResult);
    }

    [TestMethod]
    public async Task Sum_Returns16For10And6()
    {
      var response = await _httpClient.GetAsync("/sum?n1=10&n2=6");
      var stringResult = await response.Content.ReadAsStringAsync();
      var intResult = int.Parse(stringResult);

      Assert.AreEqual(16, intResult);
    }
  }
}

What’s Next?

You can learn more about integration testing ASP.NET Core 6 WebAPI applications in the documentation.

If you want me to continue this journey and, for example, add service dependencies, use mocks, or something else, let me know in the comments.

If you want to learn more about .NET development, subscribe to this channel and watch this video next.

 

Claudio Bernasconi

I'm an enthusiastic Software Engineer with a passion for teaching .NET development on YouTube, writing articles about my journey on my blog, and making people smile.