Installing TUnit
Easilyâ
Assuming you have the .NET SDK installed, simply run:
dotnet new install TUnit.Templates
dotnet new TUnit -n "YourProjectName"
A new test project will be created for you with some samples of different test types and tips. When you're ready to get going, delete them and create your own!
Manuallyâ
First create an empty .NET console application:
dotnet new console --name YourTestProjectNameHere
To that project add the TUnit package:
cd YourTestProjectNameHere
dotnet add package TUnit --prerelease
And then remove any automatically generated Program.cs or main method, as this'll be taken care of by the TUnit package.
Global Usingsâ
The TUnit package automatically configures global usings for common TUnit namespaces, so your test files don't need to include using statements for:
TUnit.Core(for[Test]attribute)TUnit.Assertions(forAssert.That())TUnit.Assertions.Extensions(for assertion methods)
This means your test files can be as simple as:
namespace MyTests;
public class MyTests // No [TestClass] needed!
{
[Test] // Available without explicit using statement
public async Task MyTest()
{
await Assert.That(true).IsTrue(); // Assert is available automatically
}
}
What's Included in the TUnit Packageâ
When you install the TUnit meta package, you automatically get several useful extensions without any additional installation:
â Built-In Extensionsâ
Microsoft.Testing.Extensions.CodeCoverage
- đ Code coverage support via
--coverageflag - đ Outputs Cobertura and XML formats
- đ Replacement for Coverlet (which is not compatible with TUnit)
Microsoft.Testing.Extensions.TrxReport
- đ TRX test report generation via
--report-trxflag - đ¤ Compatible with Azure DevOps and other CI/CD systems
This means you can run tests with coverage and reports right away:
# Run tests with code coverage
dotnet run --configuration Release --coverage
# Run tests with TRX report
dotnet run --configuration Release --report-trx
# Both coverage and report
dotnet run --configuration Release --coverage --report-trx
Important: Do not install coverlet.collector or coverlet.msbuild. These packages are incompatible with TUnit because they require the VSTest platform, while TUnit uses the modern Microsoft.Testing.Platform.
For more details, see:
That's it. We're ready to write our first test.
Your .csproj should be as simple as something like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TUnit" Version="*" />
</ItemGroup>
</Project>
If you're used to other testing frameworks, you're probably used to the package Microsoft.NET.Test.Sdk.
This should NOT be used with TUnit. It'll stop test discovery from working properly.
.NET Frameworkâ
If you are still targeting .NET Framework, TUnit will try to Polyfill some missing types that are used by the compiler, such as the ModuleInitialiserAttribute.
If you have issues with other Polyfill libraries also defining them, in your project files, you can define the property <EnableTUnitPolyfills>false</EnableTUnitPolyfills> to stop TUnit generating them for you.
Central Package Management (CPM)â
TUnit is fully compatible with NuGet Central Package Management. When CPM is enabled (via Directory.Packages.props), TUnit will automatically inject the Polyfill package using VersionOverride, so you don't need to manually add it to your Directory.Packages.props file.
If you prefer to manage the Polyfill version yourself, you can:
- Add
<PackageVersion Include="Polyfill" Version="x.x.x" />to yourDirectory.Packages.props, OR - Disable automatic injection with
<EnableTUnitPolyfills>false</EnableTUnitPolyfills>and add it manually
Embedded Polyfill Attributesâ
TUnit automatically sets <PolyUseEmbeddedAttribute>true</PolyUseEmbeddedAttribute> to ensure that Polyfill types are embedded in each project. This prevents type conflicts when using InternalsVisibleTo or when multiple projects in your solution reference Polyfill. Each project gets its own isolated copy of the polyfill types, following the recommended Polyfill consuming pattern.
You can override this behavior by setting <PolyUseEmbeddedAttribute>false</PolyUseEmbeddedAttribute> in your project file if needed.