I know that there is a lot of fuse about TUnit and I am here writing about xUnit. I might cover TUnit in the future, but for now, the topic is the v3
prerelease of xUnit!
v3
v3
is in the making since a while now and it is still in the prerelease stage. The main goal of v3
is to drop old stuff that was needed in the "good old days" and to make the framework more modern. I recently started to upgrade this very blog to use v3
and I am very happy with the results. While there are new features, I didn't really used them until now. Anyway, let's highlight a few things here.
Executable!
If you want to play around, you can easily install the template:
dotnet new install xunit.v3.templates
And create a new project:
dotnet new xunit3
The first thing you will notice is something like this inside the created csproj:
<OutputType>Exe</OutputType>
Yes, the test project is now an executable! So test runner and test project are the same thing! So you can run your tests like this:
dotnet run
For example the unit tests of this blog post will print something like this:
$ dotnet run
xUnit.net v3 In-Process Runner v0.4.0-pre.20+7ef620d780 (64-bit .NET 9.0.0-rc.1.24431.7)
Discovering: LinkDotNet.Blog.UnitTests
Discovered: LinkDotNet.Blog.UnitTests
Starting: LinkDotNet.Blog.UnitTests
Finished: LinkDotNet.Blog.UnitTests
=== TEST EXECUTION SUMMARY ===
LinkDotNet.Blog.UnitTests Total: 246, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0, Time: 0.350s
You can also print out which test-classes, traits and what not you have via the command line:
dotnet run -- -list classes
The extra --
between run
and -list
is needed to pass the arguments to the test runner and not the dotnet
cli itself.
In theory you can also create a self-contained executable and run it on a different machine without the need of the dotnet
cli at all! Hello TUnit!
Migration
The migration (at least for me) was straight forward. Instead of the xunit
package reference you have to use xunit.v3
- they wanted to make it very explicit! And as discussed earlier, I switched from library (which is the default if you don't set any OutputType
) to executable. But as of now, you don't have to do this. You can still use the library output type - but there isn't really a reason to do so.
A very detailed migration guide can be found here.
Cancellation Tokens
A pretty neat feature is the support for cancellation tokens that are passed to the test as TestContext.Current.CancellationToken
:
await Task.Delay(1000, TestContext.Current.CancellationToken);
This makes it more convenient to cancel async things if you have to! The analyzers will automatically "inform" you if you are not using the token.
Matrix theory data
Also hello TUnit! Version 3 brings also support for defining theory data in a matrix style. This is a feature that is also available in TUnit and it is very handy if you have a lot of data to test. Here is an example (shamelessly stolen from "What's New"):
public static TheoryData<int, string> MyData =
new MatrixTheoryData<int, string>(
[42, 2112, 2600],
["Hello", "World"]
);
Which will result in 6 tests:
- 42, "Hello"
- 42, "World"
- 2112, "Hello"
- 2112, "World"
- 2600, "Hello"
- 2600, "World"
IAsyncLifetime
A small quality of life "fix" is that IAsyncLifetime
directly inherits from IAsyncDisposable
. This makes it easier to implement the interface and you don't have to implement DisposeAsync
anymore "twice". If you only need to disüose of things, IAsyncDisposable
is enough! You don't have to implement IAsyncLifetime
anymore. This is only useful, if you need an InitializeAsync
call!
Conclusion and will I switch to TUnit?
I am very happy with the new features of v3
and I will definitely migrate more of my projects to it. Will I switch to TUnit? For now: No. And the main reason is simple: I love the simplicity and to a big chunk the ideology behind xUnit. Look at the following test in TUnit:
[DependsOn(nameof(Test1), [typeof(string), typeof(int)])]
public void Test2() { ... }
DependsOn
is an explicit feature. I don't like that tests depend on other tests. I do see some value - for example if you have Playwright e2e tests where it is expensive to setup everything for every test case. But besides that, I see a a BIG area of misuse. Of course I cherry-picked this example, and there are many things that are very nice! Let's see where this is headed and maybe in a few months I will switch to TUnit.