xUnit v3 update: Ability to skip tests & a new TestContext

Published: Monday 20 October 2025

xUnit 3 has been released with new assert methods and a new TestContext. Here is how you use it.

Supported frameworks

xUnit 3 is supported on the following frameworks:

  • .NET 8 (or later)
  • .NET Framework 4.7.2 (or later)

If you are on a version that is below .NET 8, you will have to stay with xUnit 2 until you update.

Creating an xUnit 3 project

You'll need to install the xUnit 3 templates to be able to create a new xUnit 3 project. You can do that by running this command line:

dotnet new install xunit.v3.templates

Once installed, you can create a new xUnit 3 project. You do that by calling:

dotnet new xunit3

Create a new xUnit 3 project in Visual Studio

When you install the xUnit 3 templates, you'll be able to create a project in Visual Studio.

Do a search for xUnit v3 Test and select the template.

Create a new xUnit 3 project in Visual Studio

Create a new xUnit 3 project in Visual Studio

Set the project name, location, solution name and framework to create the project.

Update an existing project

You'll need to remove the xunit NuGet package. You can do that by opening up the .csproj file of your xUnit test project and remove the PackageReference to that NuGet package.

With that removed, you'll need to add the xunit.v3 package and update xunit.runner.visualstudio. In Visual Studio 2022, go to Tools, NuGet Package Manager and Manage NuGet Packages for Solution.... Do a search for both xunit.v3 and xunit.runner.studio and install both packages to your xUnit project.

Add new xUnit projects to your xUnit project

Add new xUnit projects to your xUnit project

To check that the update has been successful, open up Test Explorer by going to Test and Text Explorer. If the tests run, then the update has been completed.

New assert methods for skipping tests

xUnit 3 provides new assert methods for skipping tests. We have written some unit tests with this method:

// SpeedConversionHelper.cs
public class SpeedConversionHelper
{
	public static decimal ConvertToKph(decimal mph)
	{
		return mph * 1.6093m;
	}
}

Skipping tests in xUnit 2 was somewhat limited. You could only skip tests by adding the Skip attribute in either the Fact or Theory attributes:

// SpeedConversionHelperSkipTests.cs
public class SpeedConversionHelperSkipTests
{
	[Fact(Skip = "The act is more than 160 kph")]
	public void ConvertToKph_IfOver160Kph_SkipTestInAttribute()
	{
		var act = SpeedConversionHelper.ConvertToKph(100);

		Assert.Equal(160.93m, act);
	}
}

But that has been significantly improved with xUnit 3. When Assert.Skip is called, it skips the test completely and allows you to add a reason. There are two other skip methods that allow you to add a condition on skipping a test. Assert.SkipWhen will skip the test if the condition is true and Assert.SkipUnless will skip it when it's false.

In the following unit tests, we are skipping the tests if the conversion is over 160 kph:

// SpeedConversionHelperSkipTests.cs
public class SpeedConversionHelperSkipTests
{
	[Theory]
	[InlineData(50, 80.465)]
	[InlineData(75, 120.6975)]
	[InlineData(100, 160.93)]
	public void ConvertToKph_IfOver160Kph_SkipTest(decimal mph, decimal expectedKph)
	{
		var act = SpeedConversionHelper.ConvertToKph(mph);

		if (act > 160)
		{
			Assert.Skip("The act is more than 160 kph");
		}

		Assert.Equal(expectedKph, act);
	}

	[Theory]
	[InlineData(50, 80.465)]
	[InlineData(75, 120.6975)]
	[InlineData(100, 160.93)]
	public void ConvertToKph_IfOver160Kph_SkipWhenTest(decimal mph, decimal expectedKph)
	{
		var act = SpeedConversionHelper.ConvertToKph(mph);

		Assert.SkipWhen(act > 160, "The act is more than 160 kph");        
		Assert.Equal(expectedKph, act);
	}

	[Theory]
	[InlineData(50, 80.465)]
	[InlineData(75, 120.6975)]
	[InlineData(100, 160.93)]
	public void ConvertToKph_IfOver160Kph_SkipUnlessTest(decimal mph, decimal expectedKph)
	{
		var act = SpeedConversionHelper.ConvertToKph(mph);

		Assert.SkipUnless(act <= 160, "The act is more than 160 kph");
		Assert.Equal(expectedKph, act);
	}
}

Additional skip properties in Fact and Theory

There are also additional skip properties when using the Fact or Theory attribute. You can use a static property in your test class and it to the SkipWhen and SkipUnless properties in the Fact and Theory attributes.

In this example, we've added a Debug property and set it to true if the tests are run in debug mode and set it to false for the opposite. We've then used the SkipWhen and SkipUnless to add the Debug property as a string using the nameof expression:

// SpeedConversionHelperSkipAttributeTests.cs
public class SpeedConversionHelperSkipAttributeTests
{
#if DEBUG
	public static bool Debug => true;
#else
	public static bool Debug => false;
#endif

	[Fact(Skip = "In debug", SkipWhen = nameof(Debug))]
	public void ConvertToKph_InDebug_SkipTest()
	{
		var act = SpeedConversionHelper.ConvertToKph(100);

		Assert.Equal(160.93m, act);
	}

	[Fact(Skip = "Not in debug", SkipUnless = nameof(Debug))]
	public void ConvertToKph_NotInDebug_SkipTest()
	{
		var act = SpeedConversionHelper.ConvertToKph(100);

		Assert.Equal(160.93m, act);
	}
}

You can also use a static property from another class by using the SkipType property. Here, we've added a TestProperties class and set the DebugMode property to true if it's in debug, whilst setting it to false if the opposite applies.

We then used the SkipType and the typeof expression to get the class:

// SpeedConversionHelperSkipAttributeTests.cs
public class SpeedConversionHelperSkipAttributeTests
{
	[Fact(Skip = "In debug", SkipWhen = "DebugMode", SkipType = typeof(TestProperties))]
	public void ConvertToKph_InDebugFromAnotherType_SkipTest()
	{
		var act = SpeedConversionHelper.ConvertToKph(100);

		Assert.Equal(160.93m, act);
	}
}

public class TestProperties
{
#if DEBUG
	public static bool DebugMode => true;
#else
	public static bool DebugMode => false;
#endif
}

TheoryDataRow type

xUnit 3 has introduced the TheoryDataRow type. It's a class that represents a single row of theory data and allows you to provide strongly typed parameters for attributes in your Theory test. It's used as an alternative to providing plain arrays and objects:

// SpeedConversionHelperTheoryDataRowTests.cs
public class SpeedConversionHelperTheoryDataRowTests
{
	public static IEnumerable<TheoryDataRow<decimal, decimal>> TestData = 
		[new(50m, 80.465m), new(75m, 120.6975m), new(100m, 160.93m)];

	[Theory]
	[MemberData(nameof(TestData))]
	public void ConvertToKph_WithMph_CheckCorrectKph(decimal mph, decimal expectedKph)
	{
		var act = SpeedConversionHelper.ConvertToKph(mph);

		Assert.Equal(expectedKph, act);
	}
}

Matrix theory data

The new MatrixTheoryData class allows you to can combine between 2 and 5 sets of data in a matrix to generate theory data.

Consider a list of mph decimal values that are 50 and 100 and a list of attempt int values that are 1, 2 and 3.

This would create the following parameters for your Theory test:

  • mph = 50, attempt = 1
  • mph = 50, attempt = 2
  • mph = 50, attempt = 3
  • mph = 100, attempt = 1
  • mph = 100, attempt = 2
  • mph = 100, attempt = 3

And this is how you can use it in a unit test:

// SpeedConversionHelperMatrixTheoryDataTests.cs
public class SpeedConversionHelperMatrixTheoryDataTests
{
	public static TheoryData<decimal, int> TestData =
		new MatrixTheoryData<decimal, int>(
			[50m, 100m],
			[1, 2, 3]
			);

	[Theory]
	[MemberData(nameof(TestData))]
	public void ConvertToKph_KphIsOver50_ReturnsTrue(decimal mph, int attempt)
	{
		if (attempt >= 3)
		{
			Assert.Skip("Do not run on the third attempt or beyond");
		}

		var act = SpeedConversionHelper.ConvertToKph(mph);

		Assert.True(act >= 50);
	}
}

Test context

A new TestContext class has been added. This allows you to get and set information about the current state of the test pipeline.

This example shows adding a warning if the conversion to kph is over 160. The warning also provides information about the test being run:

// SpeedConversionHelperTestContextTests.cs
public class SpeedConversionHelperTestContextTests
{
	public static IEnumerable<TheoryDataRow<decimal, decimal>> TestData = 
		[new(50m, 80.465m), new(75m, 120.6975m), new(100m, 160.93m)];

	[Theory]
	[MemberData(nameof(TestData))]
	public void ConvertToKph_WithMph_CheckCorrectKph(decimal mph, decimal expectedKph)
	{
		var act = SpeedConversionHelper.ConvertToKph(mph);

		if (act > 160)
		{
			TestContext.Current.AddWarning("${TestContext.Current.TestMethod} - The act is more than 160 kph");
		}

		Assert.Equal(expectedKph, act);
	}
}

Watch the video

You'll learn more about the new features included in xUnit 3 when you watch this video:

And you can download the code example if you want to try these for yourself.

More xUnit 3 features

We've covered some of the top new features. If you want a more in-depth list, xUnit have provided a full list of what's new in xUnit 3. This includes how to override the culture as well as other miscellaneous changes.