xUnit Testing in .NET: A Step-by-Step Guide for Beginners


Introduction

Testing is a crucial part of modern software development. As applications grow in complexity, ensuring that code behaves as expected becomes increasingly important. In the .NET ecosystem, xUnit is one of the most popular unit testing frameworks used by developers to write and execute automated tests.

What is xUnit?

xUnit.net is a free, open-source unit testing framework for .NET applications. It is the successor to NUnit and follows the xUnit testing architecture used across many programming languages.

xUnit helps developers:

  • Verify application behavior
  • Detect bugs early
  • Prevent regressions
  • Improve code quality
  • Support continuous integration pipelines

Why Use xUnit?

Some benefits of using xUnit include:

1. Simplicity

xUnit provides a clean and straightforward syntax for writing tests.

2. Strong Community Support

It is widely adopted across the .NET ecosystem.

3. Built-in Dependency Injection Support

xUnit offers better support for modern development practices.

4. Parallel Test Execution

Tests can run in parallel, improving execution speed.

5. Cross-Platform Compatibility

Works on Windows, Linux, and macOS.

Prerequisites

Before getting started, ensure you have:

  • .NET SDK installed
  • Visual Studio, Visual Studio Code, or Rider
  • Basic knowledge of C#

Verify your installation:

dotnet --version

Step 1: Create a .NET Solution

Create a new solution:

dotnet new sln -n CalculatorSolution

Navigate into the solution directory:

cd CalculatorSolution

Step 2: Create a Class Library Project

Create the application project:

dotnet new classlib -n CalculatorApp

Add it to the solution:

dotnet sln add CalculatorApp/CalculatorApp.csproj

Step 3: Create an xUnit Test Project

Create the test project:

dotnet new xunit -n CalculatorApp.Tests

Add the test project to the solution:

dotnet sln add CalculatorApp.Tests/CalculatorApp.Tests.csproj

Step 4: Add Project Reference

The test project must reference the application project.

Run:

dotnet add CalculatorApp.Tests reference CalculatorApp

Your solution structure should look like:

CalculatorSolution
│
├── CalculatorApp
│   └── Calculator.cs
│
└── CalculatorApp.Tests
    └── CalculatorTests.cs

Step 5: Create a Simple Calculator Class

Inside the CalculatorApp project, create:

namespace CalculatorApp;

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }
}

Step 6: Write Your First xUnit Test

Create a test class:

using CalculatorApp;
using Xunit;

namespace CalculatorApp.Tests;

public class CalculatorTests
{
    [Fact]
    public void Add_TwoNumbers_ReturnsSum()
    {
        // Arrange
        var calculator = new Calculator();

        // Act
        var result = calculator.Add(5, 3);

        // Assert
        Assert.Equal(8, result);
    }
}

Understanding the Test Structure

A typical unit test follows the AAA pattern:

Arrange

Prepare required objects and data.

var calculator = new Calculator();

Act

Execute the method being tested.

var result = calculator.Add(5, 3);

Assert

Verify the expected outcome.

Assert.Equal(8, result);

Step 7: Run the Tests

Execute:

dotnet test

Expected output:

Passed! 1 test passed.

Step 8: Using Fact and Theory

xUnit provides two commonly used attributes:

Fact

Used when a test has no parameters.

[Fact]
public void Subtract_ReturnsCorrectResult()
{
    var calculator = new Calculator();

    var result = calculator.Subtract(10, 4);

    Assert.Equal(6, result);
}

Theory

Used for data-driven tests.

[Theory]
[InlineData(2, 3, 5)]
[InlineData(10, 5, 15)]
[InlineData(7, 8, 15)]
public void Add_ReturnsExpectedResult(
    int a,
    int b,
    int expected)
{
    var calculator = new Calculator();

    var result = calculator.Add(a, b);

    Assert.Equal(expected, result);
}

This test runs three times using different inputs.

Common Assertions in xUnit

Equal

Assert.Equal(10, result);

NotEqual

Assert.NotEqual(5, result);

True

Assert.True(isValid);

False

Assert.False(isValid);

Null

Assert.Null(value);

NotNull

Assert.NotNull(value);

Contains

Assert.Contains("admin", roles);

Testing Exceptions

You can verify that a method throws an exception.

Example:

[Fact]
public void Divide_ByZero_ThrowsException()
{
    var calculator = new Calculator();

    Assert.Throws<DivideByZeroException>(
        () => calculator.Divide(10, 0));
}

Using Test Fixtures

When multiple tests require shared setup, use fixtures.

Example:

public class DatabaseFixture
{
    public string ConnectionString =>
        "Server=localhost;";
}
public class DatabaseTests :
    IClassFixture<DatabaseFixture>
{
    private readonly DatabaseFixture _fixture;

    public DatabaseTests(DatabaseFixture fixture)
    {
        _fixture = fixture;
    }

    [Fact]
    public void TestConnection()
    {
        Assert.NotNull(_fixture.ConnectionString);
    }
}

Mocking Dependencies with Moq

Many applications depend on databases, APIs, or external services.

Instead of calling real dependencies, use mocks.

Install Moq:

dotnet add package Moq

Example:

public interface IEmailService
{
    bool Send(string email);
}
using Moq;
using Xunit;

public class EmailTests
{
    [Fact]
    public void SendEmail_ReturnsTrue()
    {
        var mockService =
            new Mock<IEmailService>();

        mockService
            .Setup(x => x.Send(It.IsAny<string>()))
            .Returns(true);

        Assert.True(
            mockService.Object.Send(
                "test@example.com"));
    }
}

Best Practices for xUnit Testing

Keep Tests Independent

Tests should not depend on each other.

Follow Naming Conventions

A common format:

MethodName_Scenario_ExpectedResult

Example:

Add_TwoNumbers_ReturnsSum

Test One Thing at a Time

Each test should verify a single behavior.

Avoid External Dependencies

Use mocks instead of databases or APIs whenever possible.

Keep Tests Fast

Unit tests should execute quickly.

Use Arrange-Act-Assert

Maintain consistent structure for readability.

Integrating with CI/CD

xUnit integrates seamlessly with:

  • GitHub Actions
  • Azure DevOps
  • Jenkins
  • GitLab CI/CD

Example GitHub Actions command:

- name: Run Tests
  run: dotnet test

Automated testing helps catch issues before deployment.

Real-World Benefits of xUnit

Organizations use xUnit to:

  • Improve code reliability
  • Reduce production bugs
  • Enable safe refactoring
  • Increase deployment confidence
  • Support Agile and DevOps workflows

A well-tested application is easier to maintain and scale over time.

Conclusion

xUnit is one of the most powerful and widely used testing frameworks in the .NET ecosystem. By writing unit tests early and consistently, developers can improve application quality, reduce bugs, and build confidence in their code.

0 Comments Report