Skip to content
forked from thomhurst/TUnit

A modern, fast and flexible .NET testing framework

License

Notifications You must be signed in to change notification settings

Rekkonnect/TUnit

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TUnit

A modern, flexible and fast testing framework for .NET 8 and up. With Native AOT and Trimmed Single File application support included!

nuget Nuget GitHub Workflow Status (with event) GitHub last commit (branch) License

Documentation

See here: https://thomhurst.github.io/TUnit/

IDE

TUnit is built on top of the newer Microsoft.Testing.Platform, as opposed to the older VSTest platform. Because the infrastructure behind the scenes is new and different, you may need to enable some settings. This should just be a one time thing.

Visual Studio

Visual Studio is supported on the Preview version currently.

  • Install the latest preview version
  • Open Visual Studio and go to Tools > Manage Preview Features
  • Enable "Use testing platform server mode"

Rider

Rider is supported. The Enable Testing Platform support option must be selected in Settings > Build, Execution, Deployment > Unit Testing > VSTest.

VS Code

Visual Studio Code is supported.

  • Install the extension Name: C# Dev Kit
  • Go to the C# Dev Kit extension's settings
  • Enable Dotnet > Test Window > Use Testing Platform Protocol

CLI

dotnet CLI - Fully supported. Tests should be runnable with dotnet test, dotnet run, dotnet exec or executing an executable directly. See the docs for more information!

Features

  • Native AOT / Trimmed Single File application support
  • Source generated tests
  • Dependency injection support (See here)
  • Full async support
  • Parallel by default, with mechanisms to:
    • Run specific tests completely on their own
    • Run specific tests not in parallel with other specific tests
    • Limit the parallel limit on a per-test, class or assembly level
  • Test ordering (if running not in parallel)
  • Tests can depend on other tests to form chains, useful for if one test depends on state from another action
  • Easy to read assertions
  • Injectable test data via classes, methods, compile-time args, or matrices
  • Hooks before and after:
    • TestDiscover
    • TestSession
    • Assembly
    • Class
    • Test
  • Designed to avoid common pitfalls such as leaky test states
  • Ability to view and interrogate metadata and results from various assembly/class/test context objects

Installation

dotnet add package TUnit --prerelease

Example test

    private static readonly TimeOnly Midnight = TimeOnly.FromTimeSpan(TimeSpan.Zero);
    private static readonly TimeOnly Noon = TimeOnly.FromTimeSpan(TimeSpan.FromHours(12));
    
    [Test]
    public async Task IsMorning()
    {
        var time = GetTime();

        await Assert.That(time).IsAfterOrEqualTo(Midnight)
            .And.IsBefore(Noon);
    }

or with more complex test orchestration needs

    [Before(Class)]
    public static async Task ClearDatabase(ClassHookContext context) { ... }

    [After(Class)]
    public static async Task AssertDatabaseIsAsExpected(ClassHookContext context) { ... }

    [Before(Test)]
    public async Task CreatePlaywrightBrowser(TestContext context) { ... }

    [After(Test)]
    public async Task DisposePlaywrightBrowser(TestContext context) { ... }

    [Retry(3)]
    [Test, DisplayName("Register an account")]
    [MethodData(nameof(GetAuthDetails))]
    public async Task Register(string username, string password) { ... }

    [Repeat(5)]
    [Test, DependsOn(nameof(Register))]
    [MethodData(nameof(GetAuthDetails))]
    public async Task Login(string username, string password) { ... }

    [Test, DependsOn(nameof(Login), [typeof(string), typeof(string)])]
    [MethodData(nameof(GetAuthDetails))]
    public async Task DeleteAccount(string username, string password) { ... }

    [Category("Downloads")]
    [Timeout(300_000)]
    [Test, NotInParallel(Order = 1)]
    public async Task DownloadFile1() { ... }

    [Category("Downloads")]
    [Timeout(300_000)]
    [Test, NotInParallel(Order = 2)]
    public async Task DownloadFile2() { ... }

    [Repeat(10)]
    [Test]
    [Arguments(1)]
    [Arguments(2)]
    [Arguments(3)]
    [DisplayName("Go to the page numbered $page")]
    public async Task GoToPage(int page) { ... }

    [Category("Cookies")]
    [Test, Skip("Not yet built!")]
    public async Task CheckCookies() { ... }

    [Test, Explicit, WindowsOnlyTest, RetryHttpServiceUnavailable(5)]
    [Property("Some Key", "Some Value")]
    public async Task Ping() { ... }

    [Test]
    [ParallelLimit<LoadTestParallelLimit>]
    [Repeat(1000)]
    public async Task LoadHomepage() { ... }

    public static IEnumerable<(string Username, string Password)> GetAuthDetails()
    {
        yield return ("user1", "password1");
        yield return ("user2", "password2");
        yield return ("user3", "password3");
    }

    public class WindowsOnlyTestAttribute : SkipAttribute
    {
        public WindowsOnlyTestAttribute() : base("Windows only test")
        {
        }

        public override Task<bool> ShouldSkip(TestContext testContext)
        {
            return Task.FromResult(!OperatingSystem.IsWindows());
        }
    }

    public class RetryHttpServiceUnavailableAttribute : RetryAttribute
    {
        public RetryHttpServiceUnavailableAttribute(int times) : base(times)
        {
        }

        public override Task<bool> ShouldRetry(TestInformation testInformation, Exception exception, int currentRetryCount)
        {
            return Task.FromResult(exception is HttpRequestException { StatusCode: HttpStatusCode.ServiceUnavailable });
        }
    }

    public class LoadTestParallelLimit : IParallelLimit
    {
        public int Limit => 50;
    }

Motivations

TUnit is inspired by NUnit and xUnit - two of the most popular testing frameworks for .NET.

It aims to build upon the useful features of both while trying to address any pain points that they may have.

Read more here

Benchmark

Scenario: Building the test project

macos-latest


BenchmarkDotNet v0.14.0, macOS Sonoma 14.6.1 (23G93) [Darwin 23.6.0]
Apple M1 (Virtual), 1 CPU, 3 logical and 3 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD
  DefaultJob : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD


Method Mean Error StdDev
Build_TUnit 791.6 ms 15.50 ms 19.04 ms
Build_NUnit 717.6 ms 14.13 ms 15.12 ms
Build_xUnit 707.5 ms 5.88 ms 5.22 ms
Build_MSTest 758.2 ms 9.01 ms 8.43 ms

ubuntu-latest


BenchmarkDotNet v0.14.0, Ubuntu 22.04.5 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
Build_TUnit 1.482 s 0.0195 s 0.0182 s
Build_NUnit 1.358 s 0.0231 s 0.0193 s
Build_xUnit 1.369 s 0.0142 s 0.0126 s
Build_MSTest 1.387 s 0.0121 s 0.0113 s

windows-latest


BenchmarkDotNet v0.14.0, Windows 10 (10.0.20348.2700) (Hyper-V)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
Build_TUnit 1.483 s 0.0198 s 0.0185 s
Build_NUnit 1.346 s 0.0120 s 0.0112 s
Build_xUnit 1.365 s 0.0190 s 0.0169 s
Build_MSTest 1.391 s 0.0257 s 0.0240 s

Scenario: A single test that completes instantly (including spawning a new process and initialising the test framework)

macos-latest


BenchmarkDotNet v0.14.0, macOS Sonoma 14.6.1 (23G93) [Darwin 23.6.0]
Apple M1 (Virtual), 1 CPU, 3 logical and 3 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD
  DefaultJob : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD


Method Mean Error StdDev
TUnit_AOT 75.65 ms 0.283 ms 0.221 ms
TUnit 420.32 ms 7.684 ms 9.991 ms
NUnit 689.99 ms 7.732 ms 7.940 ms
xUnit 686.50 ms 13.195 ms 14.119 ms
MSTest 614.53 ms 5.170 ms 4.036 ms

ubuntu-latest


BenchmarkDotNet v0.14.0, Ubuntu 22.04.5 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
TUnit_AOT 46.50 ms 0.982 ms 2.897 ms
TUnit 743.62 ms 14.676 ms 19.593 ms
NUnit 1,313.91 ms 8.280 ms 7.745 ms
xUnit 1,295.50 ms 9.635 ms 8.542 ms
MSTest 1,171.19 ms 18.085 ms 16.916 ms

windows-latest


BenchmarkDotNet v0.14.0, Windows 10 (10.0.20348.2700) (Hyper-V)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
TUnit_AOT 79.10 ms 1.578 ms 2.107 ms
TUnit 759.36 ms 14.908 ms 21.852 ms
NUnit 1,317.70 ms 9.924 ms 9.282 ms
xUnit 1,311.95 ms 10.252 ms 9.088 ms
MSTest 1,183.50 ms 9.261 ms 8.210 ms

Scenario: A test that takes 50ms to execute, repeated 100 times (including spawning a new process and initialising the test framework)

ubuntu-latest


BenchmarkDotNet v0.14.0, Ubuntu 22.04.5 LTS (Jammy Jellyfish)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
TUnit_AOT 83.17 ms 1.538 ms 3.942 ms
TUnit 834.38 ms 16.164 ms 22.660 ms
NUnit 6,369.08 ms 9.013 ms 8.431 ms
xUnit 6,433.45 ms 16.223 ms 14.381 ms
MSTest 6,356.61 ms 20.579 ms 19.250 ms

windows-latest


BenchmarkDotNet v0.14.0, Windows 10 (10.0.20348.2700) (Hyper-V)
AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2


Method Mean Error StdDev
TUnit_AOT 131.2 ms 2.61 ms 7.18 ms
TUnit 816.4 ms 16.11 ms 22.05 ms
NUnit 7,556.4 ms 29.99 ms 28.05 ms
xUnit 7,482.3 ms 10.38 ms 9.71 ms
MSTest 7,445.9 ms 19.07 ms 17.83 ms

macos-latest


BenchmarkDotNet v0.14.0, macOS Sonoma 14.6.1 (23G93) [Darwin 23.6.0]
Apple M1 (Virtual), 1 CPU, 3 logical and 3 physical cores
.NET SDK 8.0.401
  [Host]     : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD
  DefaultJob : .NET 8.0.8 (8.0.824.36612), Arm64 RyuJIT AdvSIMD


Method Mean Error StdDev Median
TUnit_AOT 232.8 ms 15.47 ms 45.60 ms 234.0 ms
TUnit 580.2 ms 20.72 ms 61.09 ms 601.6 ms
NUnit 14,090.3 ms 276.03 ms 468.71 ms 14,100.5 ms
xUnit 14,354.0 ms 283.94 ms 512.00 ms 14,288.0 ms
MSTest 14,392.0 ms 274.21 ms 494.47 ms 14,380.0 ms

About

A modern, fast and flexible .NET testing framework

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 100.0%