Getting Started with xUnit.net

Using .NET Framework with Visual Studio

In this article, we will demonstrate getting started with xUnit.net, showing you how to write and run your first set of unit tests.

Note: The examples were done with xUnit.net v2.4.1 and Visual Studio 2019. The version numbers, paths, and Visual Studio UI may differ for you, depending on which version you're using.

Create a unit test project

Start Visual Studio, which will bring you to the start splash screen. Under "Get Started", click "Create a new project". This will bring you to the first step of the new project wizard, where you pick your project type:

In the drop down boxes, chooses your language (C#), your platform (All platforms), and your project type (Test). Scroll through the list if necessary until you find the item titled "xUnit Test Project (.NET Core)". Select it, then click "Next".

We're picking .NET Core even though we're planning to make a .NET Framework test project, because Visual Studio doesn't contain a test project template for xUnit.net for .NET Framework. We'll fix that in just a moment.

This leads you to the second part of the new project wizard:

Type a name into the "Project name" box (like "MyFirstUnitTests"). Click "Create".

After a moment, Visual Studio will launch with your newly created project.

Find the project in the Solution Explorer (it will be titled "MyFirstUnitTests"), right click it, then click "Edit Project File". This will launch the text editor for your project file. It should look something like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="coverlet.collector" Version="1.3.0">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>

</Project>

We need to make one quick change: use a target framework that indicates .NET Framework support. For example, if you want to support .NET Framework 4.8, change the TargetFramework element like so:

<TargetFramework>net48</TargetFramework>

Let's quickly review what's in this project file:

A single empty unit test was also generated into UnitTest1.cs:

using System;
using Xunit;

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

        }
    }
}

Let's make sure everything builds. Choose Build > Build Solution from the main menu. Your project should build without issue, as shown in the output window:

Build started...
1>------ Build started: Project: MyFirstUnitTests, Configuration: Debug Any CPU ------
1>MyFirstUnitTests -> C:\Dev\MyFirstUnitTests\bin\Debug\net48\MyFirstUnitTests.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

Learning to use Test Explorer

Test Explorer is the name of the window that lets you browse and run your tests from within Visual Studio. Open it by choosing Test > Test Explorer from the main menu. You should be greeted with a window that contains a hierarchy of the tests in your project, which should look something like this:

The toolbar of Test Explorer has buttons in several groups:

The main window of Test Explorer is split into two panes:

Click the left-most button on the Test Explorer toolbar (it looks like a double green arrow, titled "Run All Tests In View". This will run the single empty test, and the result should be success:

Now that we've ensured everything is working, it's time to write our first real tests.

Write your first tests

Edit UnitTest1.cs and replace the default file contents with this:

using Xunit;

namespace MyFirstUnitTests
{
    public class UnitTest1
    {
        [Fact]
        public void PassingTest()
        {
            Assert.Equal(4, Add(2, 2));
        }

        [Fact]
        public void FailingTest()
        {
            Assert.Equal(5, Add(2, 2));
        }

        int Add(int x, int y)
        {
            return x + y;
        }
    }
}

Now let's go run the tests again and see what happens:

We purposefully wrote both a passing and failing test, and we can see that the results reflect that. By clicking on the failed test, we can see a link both to the top of the unit test (at line 14), but also the failure message (we expected 5 but got 4) as well as a link to the exact line where the failure occurred (line 16).

When you edit the source file, also take note of the fact that CodeLens decorations show up which indicate not only test status (passed/failed) on the test themselves, but also on functions that are called by the code, indicating how often the code is called in tests, and a count of which of those tests have passed or failed:

Now that we've gotten your first unit tests to run, let's introduce one more way to write tests: using theories.

Write your first theory

You may have wondered why your first unit tests use an attribute named [Fact] rather than one with a more traditional name like Test. xUnit.net includes support for two different major types of unit tests: facts and theories. When describing the difference between facts and theories, we like to say:

Facts are tests which are always true. They test invariant conditions.

Theories are tests which are only true for a particular set of data.

A good example of this is testing numeric algorithms. Let's say you want to test an algorithm which determines whether a number is odd or not. If you're writing the positive-side tests (odd numbers), then feeding even numbers into the test would cause it fail, and not because the test or algorithm is wrong.

Let's add a theory to our existing facts (including a bit of bad data, so we can see it fail):

[Theory]
[InlineData(3)]
[InlineData(5)]
[InlineData(6)]
public void MyFirstTheory(int value)
{
    Assert.True(IsOdd(value));
}

bool IsOdd(int value)
{
    return value % 2 == 1;
}

This time when we run our tests, we see a second failure, for our theory that was given 6:

Although we've only written 3 test methods, the test runner actually ran 5 tests; that's because each theory with its data set is a separate test. Note also that the runner tells you exactly which set of data failed, because it includes the parameter values in the name of the test. The Test Explorer UI even shows a new level in the tree, as each row of data becomes a test result underneath its test method.

Running tests against multiple target frameworks

Sometimes, you want to write tests and ensure they run against several target application platforms. The xUnit.net test runner that we've been using supports .NET Core 1.0 or later, .NET 5.0 or later, and .NET Framework 4.5.2 or later. With a single test project, we can have our tests run against multiple target frameworks. Open the .csproj file and make the following change.

Change TargetFramework:

<PropertyGroup>
  <TargetFramework>net48</TargetFramework>
</PropertyGroup>

To TargetFrameworks:

<PropertyGroup>
  <TargetFrameworks>net48;net5.0</TargetFrameworks>
</PropertyGroup>

When you change a project file from TargetFramework to TargetFrameworks (or back), Visual Studio might show you a yellow "alert bar" which indicates that you have to reload the project for your changes to take effect. It's best to make this kind of change from a clean environment with no dirty text editors, to prevent the possibility of losing any changes.

Test Explorer supports any combination of .NET Core (including .NET 5+) and .NET Framework targets. You can even include multiple versions of the same target framework (for example, it's legal to have something like <TargetFrameworks>net452;net461;net48;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>). Application authors will typically only use a single target framework, related to the target framework the application is intended to run on. Library authors are more likely to use several target frameworks, to ensure their tests run successfully on all supported target frameworks.

The Test Explorer tree view will now show your test project multiple times, once for each target framework. You can run individual tests from individual frameworks, or you can simply run all tests for all frameworks.

Copyright © .NET Foundation. Contributions welcomed at https://github.com/xunit/xunit/tree/gh-pages.