· 5 min read

What’s New in xUnit v3? Essential Features to Supercharge Your Tests

Learn about the new features and improvements in xUnit v3.

Learn about the new features and improvements in xUnit v3.

xUnit still remains one of the most popular unit testing frameworks in the .NET ecosystem, if not the most popular. The xUnit team has been working hard to bring new features and improvements to the framework. The upcoming xUnit v3 is no exception and it is still in preview at the time of writing this post.

In this post, I’ll cover some of the new features and improvements coming in xUnit v3 which I found interesting. Note that these features are subject to change as the framework is still in preview.

In-process Console Runner

xUnit v3, now comes with an in-process console runner that allows you to build standalone executables directly from your test projects. To use the in-process console runner, you need to just add OutputType properties to your project file. Also, you can skip using the regular test SDK Microsoft.NET.Test.Sdk while using the in-process console runner.

Here is an example project file that can be used with the in-process console runner:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType> <!-- Use Exe output type -->
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="xunit.v3" Version="0.6.0-pre.7" /> <!-- Use xUnit v3 package -->
  </ItemGroup>

</Project>

If you still want to use the VSTest Runner, you still need to use the Microsoft.NET.Test.Sdk project SDK and include the xunit.runner.visualstudio package.

Here is an example project file that can be used with the VSTest Runner:

<Project Sdk="Microsoft.NET.Test.Sdk"> <!-- Use Microsoft.NET.Test.Sdk -->

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="xunit.v3" Version="0.6.0-pre.7" />
    <PackageReference Include="xunit.runner.visualstudio" Version="3.0.0-pre.49" /> <!-- VSTest Runner -->
  </ItemGroup>

</Project>

Assembly-Level Fixtures

The Assembly-Level Fixture is a new feature in xUnit v3 that allows you to share the same fixture instance across all test classes in the assembly. This can be done using the AssemblyFixture attribute. Assembly-Level Fixtures are really a step up from Collection Fixtures, which require an extra class to carry the CollectionDefinition attribute.

Here is an example of how to use the Assembly-Level Fixture:

[assembly: AssemblyFixture(typeof(MyFixture))] // Mark the fixture class as AssemblyFixture

public class MyFixture : IDisposable
{
    public MyFixture()
    {
        // Initialize the fixture
    }

    public void Dispose()
    {
        // Cleanup the fixture
    }
}

public class MyTestClass1(MyFixture fixture) // Inject the fixture into the test class
{
    [Fact]
    public void Test1()
    {
        // Use the fixture
    }
}

Once you mark the fixture class with the AssemblyFixture attribute, the fixture will be created before any tests are run and disposed of after all tests are run. Also the fixture instance can be injected into any test class in the assembly.

Test Pipeline Startup

Sometimes you need to run some code early in the test run, even before the assembly-level fixtures are created. To run things even earlier than assembly-level fixtures, you can use the new ITestPipelineStartup interface and new TestPipelineStartup attribute. They allow you to run any code before any tests are discovered or run. This can be useful for setting up global test environment settings. They also provide a way to run any cleanup code after all tests are run.

Below is an example of how to use the Test Pipeline Startup:

[assembly: TestPipelineStartup(typeof(MyTestPipelineStartup))] // Mark the pipeline class as TestPipelineStartup

public class MyTestPipelineStartup : ITestPipelineStartup
{
    public async Task StartAsync()
    {
        // Setup the pipeline
    }

    public async Task StopAsync()
    {
        // Cleanup the pipeline
    }
}

Note that you can have only one Pipeline Startup class per assembly. The StartAsync method is called before any tests are discovered or run, and the StopAsync method is called after all tests are run.

Support for Console, Debug, and Trace Output

xUnit v3 now supports capturing output from Console, Debug, and Trace output. This can be useful for debugging tests or capturing logs. You can capture the output using the ITestOutputHelper interface. Currently, they are disabled by default for backward compatibility reasons. You can enable by using assembly-level attributes like CaptureConsole and CaptureTrace.

[assembly: CaptureConsole] // Capture Console output
[assembly: CaptureTrace] // Capture Debug and Trace output

Support for Microsoft Testing Platform

xUnit v3 also supports the Microsoft Testing Platform (MSTest) which means you can run xUnit tests using the MSTest test runner. To switch using the MSTest test runner, you need to add the following property to your project file:

<PropertyGroup>
  <UseMicrosoftTestingPlatform>true</UseMicrosoftTestingPlatform>
</PropertyGroup>

Once you toggle the UseMicrosoftTestingPlatform property, the test runner will use the MSTest test runner instead of the xUnit test runner and you’ll find the commandline experience is more similar to what you see in MSTest or TUnit.

Just like xUnit v3, Microsoft Testing Platform also supports standalone executables while offering improved performance and NuGet-based extensibility model.

Explicit Tests

While writing tests, you can now mark tests as Explicit by adding Explicit = true to the Fact or Theory attribute. This can be useful when you want to exclude certain tests from the test run.

[Fact(Explicit = true)]
public void IntegrationTest1()
{
    // Test code
}

[Theory(Explicit = true)]
[InlineData(1)]
[InlineData(2)]
public void IntegrationTest2(int value)
{
    // Test code
}

This feature can be particularly handy when you want to exclude integration tests from general test runs.

Wrapping Up

These are only handful of the new features and improvements coming in xUnit v3. The majority of the focus in v3 is the fundamental changes to the core framework and the test runners, to make it more extensible and performant. More importantly, the runner utilities has a major overhaul to make it more flexible and easier to use. Besides, some of the reflection-based abstractions have been removed from the core framework. With this, I am also hoping for a full NativeAOT support in the future releases.

Although xUnit v3 is still in preview, you can try it out by installing the xunit.v3 package from NuGet or use their new v3 templates. Note that if you are having existing v2 projects, the migration path to v3 may not be straightforward and you may need to make some changes to your test projects. For more information, you can check out their migration guide here.

Back to Blog

Related Posts

View All Posts »
Writing Tests for Dapper with TestContainers in xUnit

Writing Tests for Dapper with TestContainers in xUnit

Writing tests for Dapper is traditionally challenging due to two reasons. In this post, we will explore how to write integration test fixtures for Dapper queries using TestContainers in xUnit and how to reuse the database container across multiple tests.

Using Dapper with FluentMigrator

Using Dapper with FluentMigrator

In this post, we will explore how to use Dapper with FluentMigrator for schema migrations in ASP.NET Core. We will set up a simple project, and use FluentMigrator to manage the schema migrations in code and run them during the application startup.