Skip to main content

.NET Unit Test with xUnit

· 4 min read
Guster
Full-stack Staff Engineer

image

Introduction

Unit testing in a .NET project requires a slightly different approach compared to other web frameworks like Spring Boot, Ruby on Rails, Node.js, or Laravel.

Typically, in these frameworks, we integrate a unit testing library—such as jest for Node or rspec for Rails—directly into the source project via the package manager manifest (package.json or Gemfile). These package managers often allow you to specify the environment for installing dependencies (e.g., npm install --dev).

In .NET, however, this isn't the case. To integrate unit tests, we need to restructure the project.

For example, assuming you have an existing .NET project with this structure.

/my-todo-app
my-todo-app.sln
/src
MyClass.cs

You will need to restructure the project similar to

/my-todo-app
my-todo-app.sln
/src
MyClass.cs
my-todo-app.csproj
/tests
MyClassTests.cs
test.csproj

Ensure there's no nested .csproj structure—that is, avoid placing a .csproj file inside a parent folder that already contains another .csproj. This prevents the error Duplicate global::System.Runtime.Versioning.TargetFrameworkAttribute.

Example of a well-structured .NET project

MySolution/

├── MySolution.sln # Solution file

├── src/ # Source code directory
│ ├── MyApp/ # Main application project
│ ├── MyApp.csproj # Project file for MyApp
│ ├── Program.cs # Entry point for the application
│ ├── Startup.cs # Configuration and services setup
│ ├── Controllers/ # MVC or API controllers
│ │ └── HomeController.cs # Example controller
│ ├── Models/ # Application models
│ │ └── UserModel.cs # Example model
│ └── Services/ # Business logic services
│ └── UserService.cs # Example service

└── tests/ # Test projects directory
├── MyApp.Tests/ # Unit test project for MyApp
├── MyApp.Tests.csproj # Project file for unit tests
├── UserServiceTests.cs # Unit tests for UserService
└── HomeControllerTests.cs # Unit tests for HomeController

Create a unit test

Now let's dive into how it works. Assume we have a service class AuthService that's responsible for generating and decoding JWT tokens.

public class AuthService
{
public string GenerateJwtToken(User user)
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("jwt_secret_key");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(
[
new Claim("id", user.Id.ToString())
]),
Issuer = "john",
Expires = DateTime.UtcNow.AddHours(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}

public JwtPayload DecodeJwtToken(string token)
{
var handler = new JwtSecurityTokenHandler();
var payload = handler.ReadJwtToken(token).Payload;
return payload;
}
}

Create a test class AuthServiceTest under /tests directory

public class AuthServiceTest
{
[Fact]
public void TestGenerateJwtToken()
{
var user = new User { Id = 1, Email = "lorem@ipsum.com" };
var authService = new AuthService();
var token = authService.GenerateJwtToken(user);
var decodedPayload = authService.DecodeJwtToken(token);

// assert that the JWT token is not null
Assert.NotNull(token);

// assert that the claim "id" is as expected
Assert.Equal("1", decodedPayload["id"].ToString());
}
}

Start testing

Now that we have created a sample test, let’s run it.

  • Run the following command to run all unit tests in the project

    dotnet test
  • To run a specific test, use the --filter option. For more advanced filtering techniques, visit the official documentation.

    dotnet test --filter "AuthServiceTest"
  • A summary of the test results will be shown

    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 - test.dll (net8.0)

Summary

Unit testing is an essential practice in software development, and xUnit provides a robust framework for .NET developers to implement effective tests. By following the structure and examples outlined in this guide, you can create comprehensive unit tests for your .NET applications. Remember that regular testing helps catch bugs early, improves code quality, and increases confidence in your codebase. As you become more familiar with xUnit, you'll find it an invaluable tool in your development process.