Scaffolding Unit Tests in ASP.NET Core

“Unit tests are the first line of defense against defects and are an important part of the design process.” - James Grenning, one of the original authors of the Agile Manifesto

Testing is a central part of software development. It can provide you with confidence that your application behaves as desired. This process can also detect errors before your application reaches production. You can perform manual testing or automated testing for your project, but I definitely prefer automated testing. There are many types of automated testing in software development, one of which is unit testing.

A unit test is a software development process that involves testing individual units or components of a software application. It should test a specific behavior within the production code.

In this post, I will demonstrate how to scaffold a unit test (automated testing) in ASP.NET Core MVC using xUnit. First, we will create a new ASP.NET Core MVC project. This project is about creating a simple Hogwarts House Sorting app, similar to the Sorting Hat in the Harry Potter series. The main feature of this app is that users can receive their house assignment after they submit their name.

Create Projects

Let’s open PowerShell (or Terminal if you’re using Linux or macOS) and create a folder named ‘hogwarts-house-web-app’, then change into that folder.

New-Item -Path "hogwarts-house-web-app" -ItemType Directory; cd "hogwarts-house-web-app"  

Next, create two projects, HouseSorting for our app and HouseSorting.Tests for testing. Use this command to create the HouseSorting project.

dotnet new mvc -o HouseSorting

Use the following command to create HouseSorting.Tests.

dotnet new xunit -o HouseSorting.Tests

After that, let’s put all the projects in a solution using this command. This command creates a .sln file in the current folder, with the same name as the folder.

dotnet new sln

Add the HouseSorting and HouseSorting.Tests projects to the solution file by running the following command.

dotnet sln add .\HouseSorting\HouseSorting.csproj  

For HouseSorting.Tests, use this command.

dotnet sln add .\HouseSorting.Tests\HouseSorting.Tests.csproj  

Next, add the HouseSorting as a dependency to the HouseSorting.Tests project.

dotnet add .\HouseSorting.Tests\HouseSorting.Tests.csproj reference .\HouseSorting\HouseSorting.csproj  

This is the folder structure of this solution right now.

hogwarts-house-web-app/
├── hogwarts-house-web-app.sln
├── HouseSorting
└── HouseSorting.Tests

Open “hogwarts-house-web-app” folder in VS Code (or your favorite IDE).
Let’s create SortingController inside HouseSorting\Controllers folder to handle name submission from user and return house information to user.
Update SortingController with the following code.

using Microsoft.AspNetCore.Mvc;

namespace HouseSorting.Controllers;

public class SortingController : Controller
{
  public IActionResult Index(string name)
  {
    if (!ModelState.IsValid || String.IsNullOrEmpty(name))
    {
      return BadRequest(ModelState);
    }

    string[] houses = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"];  
    Random random = new();
    var randomIndex = random.Next(houses.Length);
    ViewData["house"] = houses[randomIndex];
    ViewData["name"] = name;

    return View();
  }
}

SortingController has one method, Index method. This method receives string type name parameter. SortingController returns BadRequest when the model state is invalid and name parameter is empty string or null. Variable house contains all houses in Hogwarts. We use Random class to generate house assignment for user.

Next, create Sorting folder inside HouseSorting\Views and create Index.cshtml inside this new folder for Index Method.

Copy and paste this code to HouseSorting\Views\Sorting\Index.cshtml.

@{
  ViewData["Title"] = "House Sorting Page";
}

<h1>@ViewData["Title"]</h1>

<p>Hello @ViewData["name"]! Your house is @ViewData["house"].</p>  

From “hogwarts-house-web-app” folder, Let’s run our web app via powershell.

dotnet run --project .\HouseSorting\

If you visit /sorting route, you will see a bad request information. We need to provide name parameter for this route. You can use parameter in url like /sorting?name=Harry to see the house assigment result. Use Ctrl + C to stop our web app.

Create Tests

Before we create tests for hogwarts-house-web-app, delete default test file .\HouseSorting.Tests\UnitTest1.cs. Then, create a SortingControllerTests.cs file inside .\HouseSorting.Tests to test SortingController. Add the following code to SortingControllerTests.cs.

using HouseSorting.Controllers;
using Microsoft.AspNetCore.Mvc;

namespace HouseSorting.Tests;

public class SortingControllerTests
{
  [Fact]
  public void Index_ReturnsAViewResult_WithHouseInformation()
  {
    var controller = new SortingController();
    var result = controller.Index("Jorge Potter") as ViewResult;
    string[] houses = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"];  
    var house = result!.ViewData["house"] as string;

    Assert.IsType<ViewResult>(result);
    Assert.Contains(house, houses);
  }

  [Fact]
  public void IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
  {
    var controller = new SortingController();
    var result = controller.Index(String.Empty);
    var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
    Assert.IsType<SerializableError>(badRequestResult.Value);
  }
}

The SortingControllerTests class contains two test methods. The Index_ReturnsAViewResult_WithHouseInformation method tests the result of the Index method on SortingController. It asserts that SortingController returns a view and that the view contains ViewData about house assignment.

The IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid method tests the response when the Model State is invalid, or when the name parameter in the Index method is null or an empty string.

The [Fact] attribute declares a test method that’s run by the test runner.

From the “hogwarts-house-web-app” folder, run this command via powershell.

 dotnet test

The dotnet test command builds both projects and runs the tests. You will see that all tests passed.

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

Passed!  - Failed:     0, Passed:     2, Skipped:     0, Total:     2, Duration: 6 ms - HouseSorting.Tests.dll (net8.0)  

In this post, you will learn how to scaffold unit tests in an ASP.NET Core project and gain some insight into what unit testing in ASP.NET Core should look like. You can use this structure for your project and add testing to make your application more reliable and maintainable.

Here is my repository about this post.

Github - Hogwarts House Web App

Finally, I believe this could be your mantra for testing: ‘Code, Test, Relax.’