Introduction to xUnit

In this lesson, we’ll learn how to set up a basic automated test using xUnit. We’ll do so with the help of the following interactive playground:

namespace MainApp;

public class Calculator
{
    public int MultiplyByTwo(int value)
    {
        return value * 2;
    }
}
Basic xUnit setup

In this setup, we have the main application located in the MainApp folder. This is a basic console application. Inside it, we have the Calculator.cs file. On line 5 of this file, we have the following method, which multiplies an integer number passed to it by two:

public int MultiplyByTwo(int value)
{
return value * 2;
}
MultiplyByTwo method

We will write a test that validates this behavior.

Adding xUnit dependencies

Our executable, which contains the xUnit tests, is represented by the MainApp.Tests project. We use the same naming convention that’s often used in real-life projects. We create tests for a specific library or executable, which is MainApp in our case. Then, we use the same name in our test project but add the .Tests suffix to the name.

If we open the MainApp.Tests.csproj file inside this folder, we see on lines 13–22 the following necessary NuGet dependencies:

  • Microsoft.NET.Test.Sdk: This allows the tests to be executed by .NET.

  • xunit: This contains the core xUnit dependencies.

  • xunit.runner.visualstudio: This allows xUnit tests to be recognizable by the Test Explorer in Visual Studio IDEAn integrated development environment is a software that allows developers to write code, debug it, and build their application..

  • coverlet.collector: This allows us to collect code coverage metrics.

Then, once we add all the necessary dependencies, we need to add a reference to the project that we want to test. We do so on line 26 of the MainApp.Tests.csproj file.

Writing xUnit tests

Before using xUnit, we must add the Xunit namespace reference. We do that globally in the MainApp.Tests project. We reference it with the global using keywords in the Usings.cs file.

Our test can be found in the CalculatorTests.cs file. Here, we use a commonly accepted naming convention once again. Because we’re testing public methods in a specific class, we’ve given our test class the same name as the class we test, appending the Tests suffix to it.

If we open this file, we see that it has a single public method marked with the Fact attribute, which we can find on line 5. This is the attribute that xUnit uses to mark test methods. The test runner recognizes this method as a test it can execute. The method itself appears as follows:

Press + to interact
public void MultiplyByTwo_CanMultiply()
{
Calculator calculator = new();
int result = calculator.MultiplyByTwo(2);
Assert.Equal(4, result );
}

In this example, we give our method a descriptive name to explain what we validate in the test using the following method:

{name of the method under test}_{can perform an action}
Naming convention used for test methods

This is also a fairly common naming convention used for naming test methods. In it, we first provide the name of the method we test. Then, we add an underscore followed by a description of the capability we validate. In our case, we test the MultiplyByTwo method and validate that this method can do the multiplication.

On line 3 of the snippet above, we create an instance of the Calculator class. Then, we call the MultiplyByTwo() method and obtain the result. On line 5, we use the Assert class supplied by the testing framework to verify that the result matches the expected value, which in our case is 4. The test passes if the actual value matches the expected value. The test fails if the value is different or if an exception is thrown during the execution.

Examining the test output

We can see how the test works by clicking the “Run” button in the playground at the beginning of the lesson. Doing this builds the project, and the playground executes the dotnet test command line interface (CLI) command. The output of this command shows how many tests are executed and if all of them pass. In our case, it looks similar to the following:

Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms

Here is what the output represents:

  • Overall status: This is either Passed! or Failed!. It’s considered a failure if any of the tests fail, otherwise it passes.

  • Failed: This is the count of failed tests.

  • Passed: This is the count of passed tests.

  • Skipped: This is the count of the test methods that didn’t run because they were configured to be excluded.

  • Total: This is the total number of tests executed.

  • Duration: This is the total duration of the test execution.

Test output for failing tests

If we want to see what the output looks like when a test fails, we can add the following method to the CalculatorTests.cs file:

[Fact]
public void Fail()
{
Calculator calculator = new();
int result = calculator.MultiplyByTwo(2);
Assert.Equal(3, result );
}
Failing test method

This fails because its assertion statement is deliberately configured to expect the wrong value (3 instead of 4). If we execute this now, we should see output similar to the following:

Failed! - Failed: 1, Passed: 1, Skipped: 0, Total: 2, Duration: 65 ms

We also see the error telling us which specific test failed.