Why write tests?

Tests enable us to verify that our code behaves as expected. They also enable us to refactor our code with confidence, knowing that we haven't broken anything.

That last part is really key - refactoring with confidence is the best reason to test. It also provides a defacto "spec" for your code, describing how it should behave.

Writing our first test

We'll start by adding a test project to our solution in Visual Studio Code. Most devs these days use XUnit, so that's what we'll use. (NUnit is a good choice too - doesn't really matter, just write the darn tests!)

You'll get a test project with a unit test file already created for you:

namespace TheEmployeeAPI.Tests;

public class UnitTest1
{
    [Fact]
    public void Test1()
    {

    }
}

Let's add a reference to our API project and make a couple of modifications. Right click the project file and click Add Reference to add TheEmployeeAPI as a reference so our test project can see it.

You'll also want to install the Microsoft.AspNetCore.Mvc.Testing package to your test project. This package provides a WebApplicationFactory<Program> class that we can use to create an HttpClient instance to test our API.

You will need to make one slight change to the TheEmployeeAPI project file to get tests working. Per the Microsoft Docs, you need to either add a partial public Program class or just make internal APIs visible to your test project. The partial declaration is a bit cleaner (and the internal thing doesn't seem to work for me) so we'll do that:

Now we can add a test class to our test project:

using Microsoft.AspNetCore.Mvc.Testing;

namespace TheEmployeeAPI.Tests;

public class BasicTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public BasicTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }
}

Wait, what is WebApplicationFactory<Program>? It's a class that provides a WebApplication instance to our tests. We'll use it to create an HttpClient instance that we can use to test our API.

And what is IClassFixture<WebApplicationFactory<Program>>? It basically instructs XUnit to create an instance of WebApplicationFactory<Program> for each test class and dispose of it after the test is completed.

Let's create a test method to test our get all Employees endpoint:

[Fact]
public async Task GetAllEmployees_ReturnsOkResult()
{
    var client = _factory.CreateClient();
    var response = await client.GetAsync("/employees");

    response.EnsureSuccessStatusCode();
}

Run your test using the dotnet CLI:

dotnet test

You should see the following output:

❯ dotnet test
  Determining projects to restore...
  All projects are up-to-date for restore.
  TheEmployeeAPI -> /Users/spencer/Repos/building-apis-with-csharp-and-aspnet-core-code/2-D-refactoring-to-request-classes-and-adding-a-put-request/TheEmployeeAPI/bin/Debug/net8.0/TheEmployeeAPI.dll
  TheEmployeeAPI.Tests -> /Users/spencer/Repos/building-apis-with-csharp-and-aspnet-core-code/2-D-refactoring-to-request-classes-and-adding-a-put-request/TheEmployeeAPI.Tests/bin/Debug/net8.0/TheEmployeeAPI.Tests.dll
Test run for /Users/spencer/Repos/building-apis-with-csharp-and-aspnet-core-code/2-D-refactoring-to-request-classes-and-adding-a-put-request/TheEmployeeAPI.Tests/bin/Debug/net8.0/TheEmployeeAPI.Tests.dll (.NETCoreApp,Version=v8.0)
VSTest version 17.11.0 (arm64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - TheEmployeeAPI.Tests.dll (net8.0)

Woo! You've successfully written your first test!

Now let's do the rest of the endpoints:

//a couple of other using statements
using System.Net;
using System.Net.Http.Json;

[Fact]
public async Task GetEmployeeById_ReturnsOkResult()
{
    var client = _factory.CreateClient();
    var response = await client.GetAsync("/employees/1");

    response.EnsureSuccessStatusCode();
}

[Fact]
public async Task CreateEmployee_ReturnsCreatedResult()
{
    var client = _factory.CreateClient();
    var response = await client.PostAsJsonAsync("/employees", new Employee { FirstName = "John", LastName = "Doe" });

    response.EnsureSuccessStatusCode();
}

[Fact]
public async Task CreateEmployee_ReturnsBadRequestResult()
{
    var client = _factory.CreateClient();
    var response = await client.PostAsJsonAsync("/employees", new{});

    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

Congrats, you've successfully written tests for your API!