diff --git a/README.md b/README.md index a7daedaf1..64ef03608 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# RSCG - 238 Examples of Roslyn Source Code Generators / 15 created by Microsoft / +# RSCG - 239 Examples of Roslyn Source Code Generators / 15 created by Microsoft / -The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 238 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. +The RSCG_Examples repository is a comprehensive documentation system that automatically processes and showcases 239 Roslyn Source Code Generator (RSCG) examples. The system transforms individual RSCG projects into structured documentation with code examples and cross-referenced content with a searchable website and code example exports. This system serves as both a learning resource for .NET developers interested in source generators and an automated pipeline for maintaining up-to-date documentation about the RSCG ecosystem -## Latest Update : 2025-11-07 => 07 November 2025 +## Latest Update : 2025-11-08 => 08 November 2025 If you want to see examples with code, please click ***[List V2](https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG)*** @@ -24,8 +24,30 @@ If you want to be notified each time I add a new RSCG example , please click htt ## Content -Those are the 238 Roslyn Source Code Generators that I have tested you can see and download source code example. +Those are the 239 Roslyn Source Code Generators that I have tested you can see and download source code example. ( including 15 from Microsoft ) +### 239. [TUnit](https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit) , in the [Tests](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#tests) category + +Generated on : 2025-11-08 => 08 November 2025 + +
+ Expand + + + +Author: Tom Longhurst + + + +Nuget: [https://www.nuget.org/packages/TUnit/](https://www.nuget.org/packages/TUnit/) + + +Link: [https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit](https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit) + +Source: [https://github.com/thomhurst/TUnit](https://github.com/thomhurst/TUnit) + +
+ ### 238. [TeCLI](https://ignatandrei.github.io/RSCG_Examples/v2/docs/TeCLI) , in the [CommandLine](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#commandline) category Generated on : 2025-11-07 => 07 November 2025 diff --git a/later.md b/later.md index 9c9d60c2f..4830274f6 100644 --- a/later.md +++ b/later.md @@ -1,6 +1,6 @@ # Just later -## Latest Update : 2025-11-07 => 07 November 2025 +## Latest Update : 2025-11-08 => 08 November 2025 diff --git a/v2/.tours/TUnit.tour b/v2/.tours/TUnit.tour new file mode 100644 index 000000000..bc4b547df --- /dev/null +++ b/v2/.tours/TUnit.tour @@ -0,0 +1,48 @@ + +{ + "$schema": "https://aka.ms/codetour-schema", + "title": "TUnit", + "steps": + [ + { + "file": "rscg_examples/TUnit/src/TestDemo/TestDemo.csproj", + "description": "First, we add Nuget [TUnit](https://www.nuget.org/packages/TUnit/) in csproj ", + "pattern": "TUnit" + } + + ,{ + "file": "rscg_examples/TUnit/src/TestDemo/FirstTest.cs", + "description": "File FirstTest.cs ", + "pattern": "this is the code" + } + + + ,{ + "file": "rscg_examples/TUnit/src/TestDemo/obj/GX/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.Generators.TestMetadataGenerator/TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum.g.cs", + "description": "Generated File 4 from 4 : TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum.g.cs ", + "line": 1 + } + + ,{ + "file": "rscg_examples/TUnit/src/TestDemo/obj/GX/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.Generators.AotConverterGenerator/AotConverters.g.cs", + "description": "Generated File 3 from 4 : AotConverters.g.cs ", + "line": 1 + } + + ,{ + "file": "rscg_examples/TUnit/src/TestDemo/obj/GX/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.CodeGenerators.DisableReflectionScannerGenerator/DisableReflectionScanner.g.cs", + "description": "Generated File 2 from 4 : DisableReflectionScanner.g.cs ", + "line": 1 + } + + ,{ + "file": "rscg_examples/TUnit/src/TestDemo/obj/GX/TUnit.Core.SourceGenerator/TUnit.Core.SourceGenerator.CodeGenerators.AssemblyLoaderGenerator/AssemblyLoader.g.cs", + "description": "Generated File 1 from 4 : AssemblyLoader.g.cs ", + "line": 1 + } + + ], + + "ref": "main" + +} \ No newline at end of file diff --git a/v2/Generator/MultiGeneratorV2.cs b/v2/Generator/MultiGeneratorV2.cs index 1687cc30f..5b708be45 100644 --- a/v2/Generator/MultiGeneratorV2.cs +++ b/v2/Generator/MultiGeneratorV2.cs @@ -127,7 +127,8 @@ public string[] SourceNoRSCG() text = text.Replace("(COVERAGE", $"({d.Generator!.Source}/COVERAGE"); text = text.Replace("(TeCLI.", $"({d.Generator!.Source}/TeCLI."); - + + text = text.Replace("(assets/banner.", $"({d.Generator!.Source}/assets/banner."); text = text.Replace("(integ-tests/", $"({d.Generator!.Source}/integ-tests/"); text = text.Replace("(./samples", $"({d.Generator!.Source}/samples"); text = text.Replace("(./tests", $"({d.Generator!.Source}/tests"); diff --git a/v2/Generator/all.csv b/v2/Generator/all.csv index ed600dd9a..1f3be3649 100644 --- a/v2/Generator/all.csv +++ b/v2/Generator/all.csv @@ -237,3 +237,4 @@ Nr,Key,Source,Category 236,Validly, https://github.com/Hookyns/validly,Validator 237,Program, https://github.com/dotnet/aspnetcore/,EnhancementClass 238,TeCLI, https://github.com/tyevco/TeCLI,CommandLine +239,TUnit, https://github.com/thomhurst/TUnit,Tests diff --git a/v2/RSCGExamplesData/GeneratorDataRec.json b/v2/RSCGExamplesData/GeneratorDataRec.json index 4a5baeaf4..97adfe9a2 100644 --- a/v2/RSCGExamplesData/GeneratorDataRec.json +++ b/v2/RSCGExamplesData/GeneratorDataRec.json @@ -1441,5 +1441,11 @@ "Category":32, "dtStart": "2025-11-07T00:00:00", "show": true +}, +{ + "ID":"TUnit", + "Category":13, + "dtStart": "2025-11-08T00:00:00", + "show": true } ] \ No newline at end of file diff --git a/v2/book/examples/TUnit.html b/v2/book/examples/TUnit.html new file mode 100644 index 000000000..be9b10f1b --- /dev/null +++ b/v2/book/examples/TUnit.html @@ -0,0 +1,68 @@ + +

RSCG nr 239 : TUnit

+ +

Info

+Nuget : https://www.nuget.org/packages/TUnit/ + +

You can find more details at : https://github.com/thomhurst/TUnit

+ +

Author :Tom Longhurst

+ +

Source: https://github.com/thomhurst/TUnit

+ +

About

+ +Writing unit tests + +

+ How to use +

+

+ Add reference to the TUnit in the csproj +

+ + +

This was for me the starting code

+ +
+ I have coded the file FirstTest.cs +
+ +
+

And here are the generated files

+ +
+ The file generated is AssemblyLoader.g.cs +
+ + +
+ The file generated is DisableReflectionScanner.g.cs +
+ + +
+ The file generated is AotConverters.g.cs +
+ + +
+ The file generated is TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum.g.cs +
+ + +

+ You can download the code and this page as pdf from + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit + +

+ + +

+ You can see the whole list at + + https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG + +

+ diff --git a/v2/book/list.html b/v2/book/list.html index 0cb76a905..ebcfcfa0e 100644 --- a/v2/book/list.html +++ b/v2/book/list.html @@ -17,7 +17,7 @@

-This is the list of 238 RSCG with examples => +This is the list of 239 RSCG with examples =>

@@ -978,6 +978,10 @@

+ + + +
238 TeCLI
239TUnit
diff --git a/v2/book/pandocHTML.yaml b/v2/book/pandocHTML.yaml index ad0dcd373..4b5c4ccd4 100644 --- a/v2/book/pandocHTML.yaml +++ b/v2/book/pandocHTML.yaml @@ -252,6 +252,7 @@ input-files: - examples/validly.html - examples/Program.html - examples/TeCLI.html +- examples/TUnit.html # or you may use input-file: with a single value # defaults: diff --git a/v2/rscg_examples/TUnit/description.json b/v2/rscg_examples/TUnit/description.json new file mode 100644 index 000000000..7e0ced5f5 --- /dev/null +++ b/v2/rscg_examples/TUnit/description.json @@ -0,0 +1,22 @@ +{ + "generator":{ + "name":"TUnit", + "nuget":[ + "https://www.nuget.org/packages/TUnit/" + ], + "link":"https://github.com/thomhurst/TUnit", + "author":"Tom Longhurst", + "source":"https://github.com/thomhurst/TUnit" + }, + "data":{ + "goodFor":["Writing unit tests"], + "csprojDemo":"TestDemo.csproj", + "csFiles":["FirstTest.cs"], + "excludeDirectoryGenerated":[""], + "includeAdditionalFiles":[""] + }, + "links":{ + "blog":"", + "video":"" + } +} \ No newline at end of file diff --git a/v2/rscg_examples/TUnit/readme.txt b/v2/rscg_examples/TUnit/readme.txt new file mode 100644 index 000000000..e1a3a1f77 --- /dev/null +++ b/v2/rscg_examples/TUnit/readme.txt @@ -0,0 +1,502 @@ +![](assets/banner.png) + +# πŸš€ The Modern Testing Framework for .NET + +**TUnit** is a modern testing framework for .NET that uses **source-generated tests**, **parallel execution by default**, and **Native AOT support**. Built on Microsoft.Testing.Platform, it's faster than traditional reflection-based frameworks and gives you more control over how your tests run. + +
+ +[![thomhurst%2FTUnit | Trendshift](https://trendshift.io/api/badge/repositories/11781)](https://trendshift.io/repositories/11781) + + +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a8231644d844435eb9fd15110ea771d8)](https://app.codacy.com/gh/thomhurst/TUnit?utm_source=github.com&utm_medium=referral&utm_content=thomhurst/TUnit&utm_campaign=Badge_Grade)![GitHub Repo stars](https://img.shields.io/github/stars/thomhurst/TUnit) ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-closed-raw/thomhurst/TUnit) + [![GitHub Sponsors](https://img.shields.io/github/sponsors/thomhurst)](https://github.com/sponsors/thomhurst) [![nuget](https://img.shields.io/nuget/v/TUnit.svg)](https://www.nuget.org/packages/TUnit/) [![NuGet Downloads](https://img.shields.io/nuget/dt/TUnit)](https://www.nuget.org/packages/TUnit/) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/thomhurst/TUnit/dotnet.yml) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/thomhurst/TUnit/main) ![License](https://img.shields.io/github/license/thomhurst/TUnit) + +
+ +## Why TUnit? + +| Feature | Traditional Frameworks | **TUnit** | +|---------|----------------------|-----------| +| Test Discovery | ❌ Runtime reflection | βœ… **Compile-time generation** | +| Execution Speed | ❌ Sequential by default | βœ… **Parallel by default** | +| Modern .NET | ⚠️ Limited AOT support | βœ… **Native AOT & trimming** | +| Test Dependencies | ❌ Not supported | βœ… **`[DependsOn]` chains** | +| Resource Management | ❌ Manual lifecycle | βœ… **Automatic cleanup** | + +**Parallel by Default** - Tests run concurrently with dependency management + +**Compile-Time Discovery** - Test structure is known before runtime + +**Modern .NET Ready** - Native AOT, trimming, and latest .NET features + +**Extensible** - Customize data sources, attributes, and test behavior + +--- + +
+ +## **[Documentation](https://tunit.dev)** + +**New to TUnit?** Start with the **[Getting Started Guide](https://tunit.dev/docs/getting-started/installation)** + +**Migrating?** See the **[Migration Guides](https://tunit.dev/docs/migration/xunit)** + +**Learn more:** **[Data-Driven Testing](https://tunit.dev/docs/test-authoring/arguments)**, **[Test Dependencies](https://tunit.dev/docs/test-authoring/depends-on)**, **[Parallelism Control](https://tunit.dev/docs/parallelism/not-in-parallel)** + +
+ +--- + +## Quick Start + +### Using the Project Template (Recommended) +```bash +dotnet new install TUnit.Templates +dotnet new TUnit -n "MyTestProject" +``` + +### Manual Installation +```bash +dotnet add package TUnit --prerelease +``` + +πŸ“– **[Complete Documentation & Guides](https://tunit.dev)** + +## Key Features + + + + + + + + + + +
+ +**Performance** +- Source-generated tests (no reflection) +- Parallel execution by default +- Native AOT & trimming support +- Optimized for speed + + + +**Test Control** +- Test dependencies with `[DependsOn]` +- Parallel limits & custom scheduling +- Built-in analyzers & compile-time checks +- Custom attributes & extensible conditions + +
+ +**Data & Assertions** +- Multiple data sources (`[Arguments]`, `[Matrix]`, `[ClassData]`) +- Fluent async assertions +- Retry logic & conditional execution +- Test metadata & context + + + +**Developer Tools** +- Full dependency injection support +- Lifecycle hooks +- IDE integration (VS, Rider, VS Code) +- Documentation & examples + +
+ +## Simple Test Example + +```csharp +[Test] +public async Task User_Creation_Should_Set_Timestamp() +{ + // Arrange + var userService = new UserService(); + + // Act + var user = await userService.CreateUserAsync("john.doe@example.com"); + + // Assert - TUnit's fluent assertions + await Assert.That(user.CreatedAt) + .IsEqualTo(DateTime.Now) + .Within(TimeSpan.FromMinutes(1)); + + await Assert.That(user.Email) + .IsEqualTo("john.doe@example.com"); +} +``` + +## Data-Driven Testing + +```csharp +[Test] +[Arguments("user1@test.com", "ValidPassword123")] +[Arguments("user2@test.com", "AnotherPassword456")] +[Arguments("admin@test.com", "AdminPass789")] +public async Task User_Login_Should_Succeed(string email, string password) +{ + var result = await authService.LoginAsync(email, password); + await Assert.That(result.IsSuccess).IsTrue(); +} + +// Matrix testing - tests all combinations +[Test] +[MatrixDataSource] +public async Task Database_Operations_Work( + [Matrix("Create", "Update", "Delete")] string operation, + [Matrix("User", "Product", "Order")] string entity) +{ + await Assert.That(await ExecuteOperation(operation, entity)) + .IsTrue(); +} +``` + +## Advanced Test Orchestration + +```csharp +[Before(Class)] +public static async Task SetupDatabase(ClassHookContext context) +{ + await DatabaseHelper.InitializeAsync(); +} + +[Test, DisplayName("Register a new account")] +[MethodDataSource(nameof(GetTestUsers))] +public async Task Register_User(string username, string password) +{ + // Test implementation +} + +[Test, DependsOn(nameof(Register_User))] +[Retry(3)] // Retry on failure +public async Task Login_With_Registered_User(string username, string password) +{ + // This test runs after Register_User completes +} + +[Test] +[ParallelLimit] // Custom parallel control +[Repeat(100)] // Run 100 times +public async Task Load_Test_Homepage() +{ + // Performance testing +} + +// Custom attributes +[Test, WindowsOnly, RetryOnHttpError(5)] +public async Task Windows_Specific_Feature() +{ + // Platform-specific test with custom retry logic +} + +public class LoadTestParallelLimit : IParallelLimit +{ + public int Limit => 10; // Limit to 10 concurrent executions +} +``` + +## Custom Test Control + +```csharp +// Custom conditional execution +public class WindowsOnlyAttribute : SkipAttribute +{ + public WindowsOnlyAttribute() : base("Windows only test") { } + + public override Task ShouldSkip(TestContext testContext) + => Task.FromResult(!OperatingSystem.IsWindows()); +} + +// Custom retry logic +public class RetryOnHttpErrorAttribute : RetryAttribute +{ + public RetryOnHttpErrorAttribute(int times) : base(times) { } + + public override Task ShouldRetry(TestInformation testInformation, + Exception exception, int currentRetryCount) + => Task.FromResult(exception is HttpRequestException { StatusCode: HttpStatusCode.ServiceUnavailable }); +} +``` + +## Common Use Cases + + + + + + + +
+ +### **Unit Testing** +```csharp +[Test] +[Arguments(1, 2, 3)] +[Arguments(5, 10, 15)] +public async Task Calculate_Sum(int a, int b, int expected) +{ + await Assert.That(Calculator.Add(a, b)) + .IsEqualTo(expected); +} +``` + + + +### **Integration Testing** +```csharp +[Test, DependsOn(nameof(CreateUser))] +public async Task Login_After_Registration() +{ + // Runs after CreateUser completes + var result = await authService.Login(user); + await Assert.That(result.IsSuccess).IsTrue(); +} +``` + + + +### **Load Testing** +```csharp +[Test] +[ParallelLimit] +[Repeat(1000)] +public async Task API_Handles_Concurrent_Requests() +{ + await Assert.That(await httpClient.GetAsync("/api/health")) + .HasStatusCode(HttpStatusCode.OK); +} +``` + +
+ +## What Makes TUnit Different? + +### **Compile-Time Test Discovery** +Tests are discovered at build time, not runtime. This means faster discovery, better IDE integration, and more predictable resource management. + +### **Parallel by Default** +Tests run in parallel by default. Use `[DependsOn]` to chain tests together, and `[ParallelLimit]` to control resource usage. + +### **Extensible** +The `DataSourceGenerator` pattern and custom attribute system let you extend TUnit without modifying the framework. + +## Community & Ecosystem + +
+ +[![Downloads](https://img.shields.io/nuget/dt/TUnit?label=Downloads&color=blue)](https://www.nuget.org/packages/TUnit/) +[![Contributors](https://img.shields.io/github/contributors/thomhurst/TUnit?label=Contributors)](https://github.com/thomhurst/TUnit/graphs/contributors) +[![Discussions](https://img.shields.io/github/discussions/thomhurst/TUnit?label=Discussions)](https://github.com/thomhurst/TUnit/discussions) + +
+ +### **Resources** +- **[Official Documentation](https://tunit.dev)** - Guides, tutorials, and API reference +- **[GitHub Discussions](https://github.com/thomhurst/TUnit/discussions)** - Get help and share ideas +- **[Issue Tracking](https://github.com/thomhurst/TUnit/issues)** - Report bugs and request features +- **[Release Notes](https://github.com/thomhurst/TUnit/releases)** - Latest updates and changes + +## IDE Support + +TUnit works with all major .NET IDEs: + +### Visual Studio (2022 17.13+) +βœ… **Fully supported** - No additional configuration needed for latest versions + +βš™οΈ **Earlier versions**: Enable "Use testing platform server mode" in Tools > Manage Preview Features + +### JetBrains Rider +βœ… **Fully supported** + +βš™οΈ **Setup**: Enable "Testing Platform support" in Settings > Build, Execution, Deployment > Unit Testing > Testing Platform + +### Visual Studio Code +βœ… **Fully supported** + +βš™οΈ **Setup**: Install [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) and enable "Use Testing Platform Protocol" + +### Command Line +βœ… **Full CLI support** - Works with `dotnet test`, `dotnet run`, and direct executable execution + +## Package Options + +| Package | Use Case | +|---------|----------| +| **`TUnit`** | **Start here** - Complete testing framework (includes Core + Engine + Assertions) | +| **`TUnit.Core`** | Test libraries and shared components (no execution engine) | +| **`TUnit.Engine`** | Test execution engine and adapter (for test projects) | +| **`TUnit.Assertions`** | Standalone assertions (works with any test framework) | +| **`TUnit.Playwright`** | Playwright integration with automatic lifecycle management | + +## Migration from Other Frameworks + +**Coming from NUnit or xUnit?** TUnit uses familiar syntax with some additions: + +```csharp +// TUnit test with dependency management and retries +[Test] +[Arguments("value1")] +[Arguments("value2")] +[Retry(3)] +[ParallelLimit] +public async Task Modern_TUnit_Test(string value) { } +``` + +πŸ“– **Need help migrating?** Check our **[Migration Guides](https://tunit.dev/docs/migration/xunit)** for xUnit, NUnit, and MSTest. + +--- + +
+ +## Getting Started + +```bash +# Create a new test project +dotnet new install TUnit.Templates && dotnet new TUnit -n "MyTestProject" + +# Or add to existing project +dotnet add package TUnit --prerelease +``` + +**Learn More**: [tunit.dev](https://tunit.dev) | **Get Help**: [GitHub Discussions](https://github.com/thomhurst/TUnit/discussions) | **Star on GitHub**: [github.com/thomhurst/TUnit](https://github.com/thomhurst/TUnit) + +
+ +## Performance Benchmark + +### Scenario: Building the test project + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|------------- |-------- |--------:|---------:|---------:|--------:| +| Build_TUnit | 1.0.48 | 1.798 s | 0.0345 s | 0.0424 s | 1.785 s | +| Build_NUnit | 4.4.0 | 1.575 s | 0.0169 s | 0.0158 s | 1.573 s | +| Build_MSTest | 4.0.1 | 1.659 s | 0.0150 s | 0.0140 s | 1.658 s | +| Build_xUnit3 | 3.2.0 | 1.579 s | 0.0182 s | 0.0170 s | 1.575 s | + + +### Scenario: Tests running asynchronous operations and async/await patterns + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |---------:|---------:|---------:|---------:| +| TUnit | 1.0.48 | 562.4 ms | 4.18 ms | 3.91 ms | 561.0 ms | +| NUnit | 4.4.0 | 679.2 ms | 7.24 ms | 6.41 ms | 679.6 ms | +| MSTest | 4.0.1 | 647.7 ms | 8.63 ms | 7.65 ms | 647.8 ms | +| xUnit3 | 3.2.0 | 744.4 ms | 11.90 ms | 10.55 ms | 741.5 ms | +| TUnit_AOT | 1.0.48 | 127.6 ms | 0.45 ms | 0.42 ms | 127.6 ms | + + +### Scenario: Parameterized tests with multiple test cases using data attributes + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |----------:|----------:|----------:|----------:| +| TUnit | 1.0.48 | 476.97 ms | 5.430 ms | 5.080 ms | 478.26 ms | +| NUnit | 4.4.0 | 537.80 ms | 6.692 ms | 6.260 ms | 537.55 ms | +| MSTest | 4.0.1 | 496.84 ms | 9.188 ms | 8.145 ms | 496.37 ms | +| xUnit3 | 3.2.0 | 584.15 ms | 10.733 ms | 10.039 ms | 582.13 ms | +| TUnit_AOT | 1.0.48 | 24.65 ms | 0.177 ms | 0.157 ms | 24.68 ms | + + +### Scenario: Tests executing massively parallel workloads with CPU-bound, I/O-bound, and mixed operations + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |-----------:|---------:|---------:|-----------:| +| TUnit | 1.0.48 | 578.1 ms | 5.79 ms | 5.13 ms | 577.3 ms | +| NUnit | 4.4.0 | 1,220.5 ms | 7.43 ms | 6.20 ms | 1,220.9 ms | +| MSTest | 4.0.1 | 3,005.3 ms | 13.91 ms | 12.33 ms | 3,003.4 ms | +| xUnit3 | 3.2.0 | 3,096.0 ms | 11.11 ms | 10.40 ms | 3,094.6 ms | +| TUnit_AOT | 1.0.48 | 130.6 ms | 0.39 ms | 0.36 ms | 130.7 ms | + + +### Scenario: Tests with complex parameter combinations creating 25-125 test variations + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |------------:|---------:|---------:|------------:| +| TUnit | 1.0.48 | 544.97 ms | 5.561 ms | 4.930 ms | 544.99 ms | +| NUnit | 4.4.0 | 1,540.60 ms | 5.644 ms | 4.713 ms | 1,540.91 ms | +| MSTest | 4.0.1 | 1,499.17 ms | 4.590 ms | 3.833 ms | 1,499.42 ms | +| xUnit3 | 3.2.0 | 1,591.72 ms | 6.560 ms | 6.136 ms | 1,592.55 ms | +| TUnit_AOT | 1.0.48 | 79.41 ms | 0.252 ms | 0.236 ms | 79.48 ms | + + +### Scenario: Large-scale parameterized tests with 100+ test cases testing framework scalability + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |----------:|----------:|----------:|----------:| +| TUnit | 1.0.48 | 498.22 ms | 7.188 ms | 6.723 ms | 496.45 ms | +| NUnit | 4.4.0 | 584.65 ms | 11.669 ms | 11.461 ms | 582.38 ms | +| MSTest | 4.0.1 | 580.53 ms | 11.233 ms | 15.747 ms | 583.58 ms | +| xUnit3 | 3.2.0 | 587.89 ms | 8.750 ms | 7.757 ms | 586.15 ms | +| TUnit_AOT | 1.0.48 | 46.75 ms | 1.442 ms | 4.253 ms | 47.33 ms | + + + diff --git a/v2/rscg_examples/TUnit/src/TestDemo.slnx b/v2/rscg_examples/TUnit/src/TestDemo.slnx new file mode 100644 index 000000000..561f6f460 --- /dev/null +++ b/v2/rscg_examples/TUnit/src/TestDemo.slnx @@ -0,0 +1,3 @@ + + + diff --git a/v2/rscg_examples/TUnit/src/TestDemo/FirstTest.cs b/v2/rscg_examples/TUnit/src/TestDemo/FirstTest.cs new file mode 100644 index 000000000..fa5122304 --- /dev/null +++ b/v2/rscg_examples/TUnit/src/TestDemo/FirstTest.cs @@ -0,0 +1,13 @@ +ο»Ώnamespace TestDemo; + + +public class FirstTest +{ + [Test] + public async Task Add_WithTwoNumbers_ReturnsSum() + { + var result = true; + // Assert + await Assert.That(result).IsTrue(); + } +} diff --git a/v2/rscg_examples/TUnit/src/TestDemo/TestDemo.csproj b/v2/rscg_examples/TUnit/src/TestDemo/TestDemo.csproj new file mode 100644 index 000000000..5bd5fae2e --- /dev/null +++ b/v2/rscg_examples/TUnit/src/TestDemo/TestDemo.csproj @@ -0,0 +1,18 @@ +ο»Ώ + + + Exe + net10.0 + enable + enable + + + + + + + + true + $(BaseIntermediateOutputPath)\GX + + diff --git a/v2/rscg_examples/TUnit/video.json b/v2/rscg_examples/TUnit/video.json new file mode 100644 index 000000000..5caa1e307 --- /dev/null +++ b/v2/rscg_examples/TUnit/video.json @@ -0,0 +1,39 @@ +{ + "scriptName": "TUnit", + "steps": +[ + {"typeStep":"exec","arg":"clipchamp.exe launch"}, + {"typeStep":"text","arg": "Welcome to Roslyn Examples"}, + {"typeStep":"text","arg":"If you want to see more examples , see List Of RSCG"}, + {"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/List-of-RSCG"}, + {"typeStep":"text","arg": "My name is Andrei Ignat and I am deeply fond of Roslyn Source Code Generator. "}, + +{"typeStep":"text","arg": "Today I will present TUnit . Writing unit tests ."}, +{"typeStep":"browser","arg":"https://www.nuget.org/packages/TUnit/"}, +{"typeStep":"text","arg": "The whole example is here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit"}, +{"typeStep":"text","arg": "You can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit#download-example-net--c-"}, +{"typeStep":"text","arg":"Here is the code downloaded "}, +{"typeStep":"exec","arg":"explorer.exe /select,D:\\gth\\RSCG_Examples\\v2\\Generator.sln"}, +{"typeStep":"text","arg": "So , let's start the project with Visual Studio Code "}, +{"typeStep":"stepvscode","arg": "-n D:\\gth\\RSCG_Examples\\v2"}, + +{"typeStep":"text","arg": "To use it ,you will put the Nuget TUnit into the csproj "}, + +{"typeStep":"stepvscode","arg": "-r -g D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\TUnit\\src\\TestDemo\\TestDemo.csproj"}, + +{"typeStep":"text","arg": "And now I will show you an example of using TUnit"}, + +{"typeStep":"hide","arg": "now execute the tour in VSCode"}, +{"typeStep":"tour", "arg": "src/.tours/"}, +{"typeStep":"text","arg":" And I will execute the project"}, +{"typeStep":"showproj", "arg":"TestDemo.csproj"}, +{"typeStep":"text","arg":" This concludes the project"}, +{"typeStep":"waitseconds","arg":"30"}, +{"typeStep":"text","arg": "Remember, you can download the code from here"}, +{"typeStep":"browser","arg":"https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit#download-example-net--c-", +SpeakTest=" "}, +{"typeStep":"waitseconds","arg":"30"}, +] +} diff --git a/v2/rscg_examples_site/docs/Categories/Tests.md b/v2/rscg_examples_site/docs/Categories/Tests.md index 0ac7c9a90..e3a14794b 100644 --- a/v2/rscg_examples_site/docs/Categories/Tests.md +++ b/v2/rscg_examples_site/docs/Categories/Tests.md @@ -1,6 +1,6 @@

Tests

-Number RSCG: 5 +Number RSCG: 6 1 [mocklis](/docs/mocklis) [![Nuget](https://img.shields.io/nuget/dt/mocklis?label=mocklis)](https://www.nuget.org/packages/mocklis/) ![GitHub Repo stars](https://img.shields.io/github/stars/mocklis/mocklis?style=social) 2024-01-03 @@ -11,4 +11,6 @@ Number RSCG: 5 4 [Ridge](/docs/Ridge) [![Nuget](https://img.shields.io/nuget/dt/Ridge?label=Ridge)](https://www.nuget.org/packages/Ridge/) ![GitHub Repo stars](https://img.shields.io/github/stars/Melchy/Ridge?style=social) 2023-08-20 5 [Rocks](/docs/Rocks) [![Nuget](https://img.shields.io/nuget/dt/Rocks?label=Rocks)](https://www.nuget.org/packages/Rocks/) ![GitHub Repo stars](https://img.shields.io/github/stars/JasonBock/Rocks?style=social) 2023-04-16 + + 6 [TUnit](/docs/TUnit) [![Nuget](https://img.shields.io/nuget/dt/TUnit?label=TUnit)](https://www.nuget.org/packages/TUnit/) ![GitHub Repo stars](https://img.shields.io/github/stars/thomhurst/TUnit?style=social) 2025-11-08 \ No newline at end of file diff --git a/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx b/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx index e80eecffd..e818cd88e 100644 --- a/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx +++ b/v2/rscg_examples_site/docs/Categories/_PrimitiveTests.mdx @@ -10,6 +10,8 @@ 5 [Rocks](/docs/Rocks) [![Nuget](https://img.shields.io/nuget/dt/Rocks?label=Rocks)](https://www.nuget.org/packages/Rocks/) ![GitHub Repo stars](https://img.shields.io/github/stars/JasonBock/Rocks?style=social) 2023-04-16 + 6 [TUnit](/docs/TUnit) [![Nuget](https://img.shields.io/nuget/dt/TUnit?label=TUnit)](https://www.nuget.org/packages/TUnit/) ![GitHub Repo stars](https://img.shields.io/github/stars/thomhurst/TUnit?style=social) 2025-11-08 + ### See category [Tests](/docs/Categories/Tests) diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/TUnit.md b/v2/rscg_examples_site/docs/RSCG-Examples/TUnit.md new file mode 100644 index 000000000..e7c4decfa --- /dev/null +++ b/v2/rscg_examples_site/docs/RSCG-Examples/TUnit.md @@ -0,0 +1,811 @@ +--- +sidebar_position: 2390 +title: 239 - TUnit +description: Writing unit tests +slug: /TUnit +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import TOCInline from '@theme/TOCInline'; +import SameCategory from '../Categories/_PrimitiveTests.mdx'; + +# TUnit by Tom Longhurst + + + + +## NuGet / site data +[![Nuget](https://img.shields.io/nuget/dt/TUnit?label=TUnit)](https://www.nuget.org/packages/TUnit/) +[![GitHub last commit](https://img.shields.io/github/last-commit/thomhurst/TUnit?label=updated)](https://github.com/thomhurst/TUnit) +![GitHub Repo stars](https://img.shields.io/github/stars/thomhurst/TUnit?style=social) + +## Details + +### Info +:::info + +Name: **TUnit** + + + +Author: Tom Longhurst + +NuGet: +*https://www.nuget.org/packages/TUnit/* + + +You can find more details at https://github.com/thomhurst/TUnit + +Source: https://github.com/thomhurst/TUnit + +::: + +### Author +:::note +Tom Longhurst +![Alt text](https://github.com/thomhurst.png) +::: + +### Original Readme +:::note + +![](https://github.com/thomhurst/TUnit/assets/banner.png) + +# πŸš€ The Modern Testing Framework for .NET + +**TUnit** is a modern testing framework for .NET that uses **source-generated tests**, **parallel execution by default**, and **Native AOT support**. Built on Microsoft.Testing.Platform, it's faster than traditional reflection-based frameworks and gives you more control over how your tests run. + +
+ +[![thomhurst%2FTUnit | Trendshift](https://trendshift.io/api/badge/repositories/11781)](https://trendshift.io/repositories/11781) + + +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a8231644d844435eb9fd15110ea771d8)](https://app.codacy.com/gh/thomhurst/TUnit?utm_source=github.com&utm_medium=referral&utm_content=thomhurst/TUnit&utm_campaign=Badge_Grade)![GitHub Repo stars](https://img.shields.io/github/stars/thomhurst/TUnit) ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues-closed-raw/thomhurst/TUnit) + [![GitHub Sponsors](https://img.shields.io/github/sponsors/thomhurst)](https://github.com/sponsors/thomhurst) [![nuget](https://img.shields.io/nuget/v/TUnit.svg)](https://www.nuget.org/packages/TUnit/) [![NuGet Downloads](https://img.shields.io/nuget/dt/TUnit)](https://www.nuget.org/packages/TUnit/) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/thomhurst/TUnit/dotnet.yml) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/thomhurst/TUnit/main) ![License](https://img.shields.io/github/license/thomhurst/TUnit) + +
+ +## Why TUnit? + +| Feature | Traditional Frameworks | **TUnit** | +|---------|----------------------|-----------| +| Test Discovery | ❌ Runtime reflection | βœ… **Compile-time generation** | +| Execution Speed | ❌ Sequential by default | βœ… **Parallel by default** | +| Modern .NET | ⚠️ Limited AOT support | βœ… **Native AOT & trimming** | +| Test Dependencies | ❌ Not supported | βœ… **`[DependsOn]` chains** | +| Resource Management | ❌ Manual lifecycle | βœ… **Automatic cleanup** | + +**Parallel by Default** - Tests run concurrently with dependency management + +**Compile-Time Discovery** - Test structure is known before runtime + +**Modern .NET Ready** - Native AOT, trimming, and latest .NET features + +**Extensible** - Customize data sources, attributes, and test behavior + +--- + +
+ +## **[Documentation](https://tunit.dev)** + +**New to TUnit?** Start with the **[Getting Started Guide](https://tunit.dev/docs/getting-started/installation)** + +**Migrating?** See the **[Migration Guides](https://tunit.dev/docs/migration/xunit)** + +**Learn more:** **[Data-Driven Testing](https://tunit.dev/docs/test-authoring/arguments)**, **[Test Dependencies](https://tunit.dev/docs/test-authoring/depends-on)**, **[Parallelism Control](https://tunit.dev/docs/parallelism/not-in-parallel)** + +
+ +--- + +## Quick Start + +### Using the Project Template (Recommended) +```bash +dotnet new install TUnit.Templates +dotnet new TUnit -n "MyTestProject" +``` + +### Manual Installation +```bash +dotnet add package TUnit --prerelease +``` + +πŸ“– **[Complete Documentation & Guides](https://tunit.dev)** + +## Key Features + + + + + + + + + + +
+ +**Performance** +- Source-generated tests (no reflection) +- Parallel execution by default +- Native AOT & trimming support +- Optimized for speed + + + +**Test Control** +- Test dependencies with `[DependsOn]` +- Parallel limits & custom scheduling +- Built-in analyzers & compile-time checks +- Custom attributes & extensible conditions + +
+ +**Data & Assertions** +- Multiple data sources (`[Arguments]`, `[Matrix]`, `[ClassData]`) +- Fluent async assertions +- Retry logic & conditional execution +- Test metadata & context + + + +**Developer Tools** +- Full dependency injection support +- Lifecycle hooks +- IDE integration (VS, Rider, VS Code) +- Documentation & examples + +
+ +## Simple Test Example + +```csharp +[Test] +public async Task User_Creation_Should_Set_Timestamp() +{ + // Arrange + var userService = new UserService(); + + // Act + var user = await userService.CreateUserAsync("john.doe@example.com"); + + // Assert - TUnit's fluent assertions + await Assert.That(user.CreatedAt) + .IsEqualTo(DateTime.Now) + .Within(TimeSpan.FromMinutes(1)); + + await Assert.That(user.Email) + .IsEqualTo("john.doe@example.com"); +} +``` + +## Data-Driven Testing + +```csharp +[Test] +[Arguments("user1@test.com", "ValidPassword123")] +[Arguments("user2@test.com", "AnotherPassword456")] +[Arguments("admin@test.com", "AdminPass789")] +public async Task User_Login_Should_Succeed(string email, string password) +{ + var result = await authService.LoginAsync(email, password); + await Assert.That(result.IsSuccess).IsTrue(); +} + +// Matrix testing - tests all combinations +[Test] +[MatrixDataSource] +public async Task Database_Operations_Work( + [Matrix("Create", "Update", "Delete")] string operation, + [Matrix("User", "Product", "Order")] string entity) +{ + await Assert.That(await ExecuteOperation(operation, entity)) + .IsTrue(); +} +``` + +## Advanced Test Orchestration + +```csharp +[Before(Class)] +public static async Task SetupDatabase(ClassHookContext context) +{ + await DatabaseHelper.InitializeAsync(); +} + +[Test, DisplayName("Register a new account")] +[MethodDataSource(nameof(GetTestUsers))] +public async Task Register_User(string username, string password) +{ + // Test implementation +} + +[Test, DependsOn(nameof(Register_User))] +[Retry(3)] // Retry on failure +public async Task Login_With_Registered_User(string username, string password) +{ + // This test runs after Register_User completes +} + +[Test] +[ParallelLimit] // Custom parallel control +[Repeat(100)] // Run 100 times +public async Task Load_Test_Homepage() +{ + // Performance testing +} + +// Custom attributes +[Test, WindowsOnly, RetryOnHttpError(5)] +public async Task Windows_Specific_Feature() +{ + // Platform-specific test with custom retry logic +} + +public class LoadTestParallelLimit : IParallelLimit +{ + public int Limit => 10; // Limit to 10 concurrent executions +} +``` + +## Custom Test Control + +```csharp +// Custom conditional execution +public class WindowsOnlyAttribute : SkipAttribute +{ + public WindowsOnlyAttribute() : base("Windows only test") \{ } + + public override Task ShouldSkip(TestContext testContext) + => Task.FromResult(!OperatingSystem.IsWindows()); +} + +// Custom retry logic +public class RetryOnHttpErrorAttribute : RetryAttribute +{ + public RetryOnHttpErrorAttribute(int times) : base(times) \{ } + + public override Task ShouldRetry(TestInformation testInformation, + Exception exception, int currentRetryCount) + => Task.FromResult(exception is HttpRequestException \{ StatusCode: HttpStatusCode.ServiceUnavailable }); +} +``` + +## Common Use Cases + + + + + + + +
+ +### **Unit Testing** +```csharp +[Test] +[Arguments(1, 2, 3)] +[Arguments(5, 10, 15)] +public async Task Calculate_Sum(int a, int b, int expected) +{ + await Assert.That(Calculator.Add(a, b)) + .IsEqualTo(expected); +} +``` + + + +### **Integration Testing** +```csharp +[Test, DependsOn(nameof(CreateUser))] +public async Task Login_After_Registration() +{ + // Runs after CreateUser completes + var result = await authService.Login(user); + await Assert.That(result.IsSuccess).IsTrue(); +} +``` + + + +### **Load Testing** +```csharp +[Test] +[ParallelLimit] +[Repeat(1000)] +public async Task API_Handles_Concurrent_Requests() +{ + await Assert.That(await httpClient.GetAsync("/api/health")) + .HasStatusCode(HttpStatusCode.OK); +} +``` + +
+ +## What Makes TUnit Different? + +### **Compile-Time Test Discovery** +Tests are discovered at build time, not runtime. This means faster discovery, better IDE integration, and more predictable resource management. + +### **Parallel by Default** +Tests run in parallel by default. Use `[DependsOn]` to chain tests together, and `[ParallelLimit]` to control resource usage. + +### **Extensible** +The `DataSourceGenerator` pattern and custom attribute system let you extend TUnit without modifying the framework. + +## Community & Ecosystem + +
+ +[![Downloads](https://img.shields.io/nuget/dt/TUnit?label=Downloads&color=blue)](https://www.nuget.org/packages/TUnit/) +[![Contributors](https://img.shields.io/github/contributors/thomhurst/TUnit?label=Contributors)](https://github.com/thomhurst/TUnit/graphs/contributors) +[![Discussions](https://img.shields.io/github/discussions/thomhurst/TUnit?label=Discussions)](https://github.com/thomhurst/TUnit/discussions) + +
+ +### **Resources** +- **[Official Documentation](https://tunit.dev)** - Guides, tutorials, and API reference +- **[GitHub Discussions](https://github.com/thomhurst/TUnit/discussions)** - Get help and share ideas +- **[Issue Tracking](https://github.com/thomhurst/TUnit/issues)** - Report bugs and request features +- **[Release Notes](https://github.com/thomhurst/TUnit/releases)** - Latest updates and changes + +## IDE Support + +TUnit works with all major .NET IDEs: + +### Visual Studio (2022 17.13+) +βœ… **Fully supported** - No additional configuration needed for latest versions + +βš™οΈ **Earlier versions**: Enable "Use testing platform server mode" in Tools > Manage Preview Features + +### JetBrains Rider +βœ… **Fully supported** + +βš™οΈ **Setup**: Enable "Testing Platform support" in Settings > Build, Execution, Deployment > Unit Testing > Testing Platform + +### Visual Studio Code +βœ… **Fully supported** + +βš™οΈ **Setup**: Install [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) and enable "Use Testing Platform Protocol" + +### Command Line +βœ… **Full CLI support** - Works with `dotnet test`, `dotnet run`, and direct executable execution + +## Package Options + +| Package | Use Case | +|---------|----------| +| **`TUnit`** | **Start here** - Complete testing framework (includes Core + Engine + Assertions) | +| **`TUnit.Core`** | Test libraries and shared components (no execution engine) | +| **`TUnit.Engine`** | Test execution engine and adapter (for test projects) | +| **`TUnit.Assertions`** | Standalone assertions (works with any test framework) | +| **`TUnit.Playwright`** | Playwright integration with automatic lifecycle management | + +## Migration from Other Frameworks + +**Coming from NUnit or xUnit?** TUnit uses familiar syntax with some additions: + +```csharp +// TUnit test with dependency management and retries +[Test] +[Arguments("value1")] +[Arguments("value2")] +[Retry(3)] +[ParallelLimit] +public async Task Modern_TUnit_Test(string value) \{ } +``` + +πŸ“– **Need help migrating?** Check our **[Migration Guides](https://tunit.dev/docs/migration/xunit)** for xUnit, NUnit, and MSTest. + +--- + +
+ +## Getting Started + +```bash +# Create a new test project +dotnet new install TUnit.Templates && dotnet new TUnit -n "MyTestProject" + +# Or add to existing project +dotnet add package TUnit --prerelease +``` + +**Learn More**: [tunit.dev](https://tunit.dev) | **Get Help**: [GitHub Discussions](https://github.com/thomhurst/TUnit/discussions) | **Star on GitHub**: [github.com/thomhurst/TUnit](https://github.com/thomhurst/TUnit) + +
+ +## Performance Benchmark + +### Scenario: Building the test project + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|------------- |-------- |--------:|---------:|---------:|--------:| +| Build_TUnit | 1.0.48 | 1.798 s | 0.0345 s | 0.0424 s | 1.785 s | +| Build_NUnit | 4.4.0 | 1.575 s | 0.0169 s | 0.0158 s | 1.573 s | +| Build_MSTest | 4.0.1 | 1.659 s | 0.0150 s | 0.0140 s | 1.658 s | +| Build_xUnit3 | 3.2.0 | 1.579 s | 0.0182 s | 0.0170 s | 1.575 s | + + +### Scenario: Tests running asynchronous operations and async/await patterns + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |---------:|---------:|---------:|---------:| +| TUnit | 1.0.48 | 562.4 ms | 4.18 ms | 3.91 ms | 561.0 ms | +| NUnit | 4.4.0 | 679.2 ms | 7.24 ms | 6.41 ms | 679.6 ms | +| MSTest | 4.0.1 | 647.7 ms | 8.63 ms | 7.65 ms | 647.8 ms | +| xUnit3 | 3.2.0 | 744.4 ms | 11.90 ms | 10.55 ms | 741.5 ms | +| TUnit_AOT | 1.0.48 | 127.6 ms | 0.45 ms | 0.42 ms | 127.6 ms | + + +### Scenario: Parameterized tests with multiple test cases using data attributes + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |----------:|----------:|----------:|----------:| +| TUnit | 1.0.48 | 476.97 ms | 5.430 ms | 5.080 ms | 478.26 ms | +| NUnit | 4.4.0 | 537.80 ms | 6.692 ms | 6.260 ms | 537.55 ms | +| MSTest | 4.0.1 | 496.84 ms | 9.188 ms | 8.145 ms | 496.37 ms | +| xUnit3 | 3.2.0 | 584.15 ms | 10.733 ms | 10.039 ms | 582.13 ms | +| TUnit_AOT | 1.0.48 | 24.65 ms | 0.177 ms | 0.157 ms | 24.68 ms | + + +### Scenario: Tests executing massively parallel workloads with CPU-bound, I/O-bound, and mixed operations + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |-----------:|---------:|---------:|-----------:| +| TUnit | 1.0.48 | 578.1 ms | 5.79 ms | 5.13 ms | 577.3 ms | +| NUnit | 4.4.0 | 1,220.5 ms | 7.43 ms | 6.20 ms | 1,220.9 ms | +| MSTest | 4.0.1 | 3,005.3 ms | 13.91 ms | 12.33 ms | 3,003.4 ms | +| xUnit3 | 3.2.0 | 3,096.0 ms | 11.11 ms | 10.40 ms | 3,094.6 ms | +| TUnit_AOT | 1.0.48 | 130.6 ms | 0.39 ms | 0.36 ms | 130.7 ms | + + +### Scenario: Tests with complex parameter combinations creating 25-125 test variations + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |------------:|---------:|---------:|------------:| +| TUnit | 1.0.48 | 544.97 ms | 5.561 ms | 4.930 ms | 544.99 ms | +| NUnit | 4.4.0 | 1,540.60 ms | 5.644 ms | 4.713 ms | 1,540.91 ms | +| MSTest | 4.0.1 | 1,499.17 ms | 4.590 ms | 3.833 ms | 1,499.42 ms | +| xUnit3 | 3.2.0 | 1,591.72 ms | 6.560 ms | 6.136 ms | 1,592.55 ms | +| TUnit_AOT | 1.0.48 | 79.41 ms | 0.252 ms | 0.236 ms | 79.48 ms | + + +### Scenario: Large-scale parameterized tests with 100+ test cases testing framework scalability + +``` + +BenchmarkDotNet v0.15.6, Linux Ubuntu 24.04.3 LTS (Noble Numbat) +AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores +.NET SDK 10.0.100-rc.2.25502.107 + [Host] : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + Job-GVKUBM : .NET 10.0.0 (10.0.0-rc.2.25502.107, 10.0.25.50307), X64 RyuJIT x86-64-v3 + +Runtime=.NET 10.0 + +``` +| Method | Version | Mean | Error | StdDev | Median | +|---------- |-------- |----------:|----------:|----------:|----------:| +| TUnit | 1.0.48 | 498.22 ms | 7.188 ms | 6.723 ms | 496.45 ms | +| NUnit | 4.4.0 | 584.65 ms | 11.669 ms | 11.461 ms | 582.38 ms | +| MSTest | 4.0.1 | 580.53 ms | 11.233 ms | 15.747 ms | 583.58 ms | +| xUnit3 | 3.2.0 | 587.89 ms | 8.750 ms | 7.757 ms | 586.15 ms | +| TUnit_AOT | 1.0.48 | 46.75 ms | 1.442 ms | 4.253 ms | 47.33 ms | + + + + + +::: + +### About +:::note + +Writing unit tests + + +::: + +## How to use + +### Example (source csproj, source files) + + + + + +This is the CSharp Project that references **TUnit** +```xml showLineNumbers {11} + + + + Exe + net10.0 + enable + enable + + + + + + + + true + $(BaseIntermediateOutputPath)\GX + + + +``` + + + + + + This is the use of **TUnit** in *FirstTest.cs* + +```csharp showLineNumbers +namespace TestDemo; + + +public class FirstTest +{ + [Test] + public async Task Add_WithTwoNumbers_ReturnsSum() + { + var result = true; + // Assert + await Assert.That(result).IsTrue(); + } +} + +``` + + + + +### Generated Files + +Those are taken from $(BaseIntermediateOutputPath)\GX + + + + +```csharp showLineNumbers +// +#pragma warning disable + +[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "1.0.0.0")] +file static class AssemblyLoader0fc44d9acf154c74aee2a9e062a8edcd +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("EnumerableAsyncProcessor, Version=3.8.4.0, Culture=neutral, PublicKeyToken=7a7adb9c614908c9")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Microsoft.Extensions.DependencyModel, Version=6.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Mono.Cecil, Version=0.11.5.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Mono.Cecil.Mdb, Version=0.11.5.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Mono.Cecil.Pdb, Version=0.11.5.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("Mono.Cecil.Rocks, Version=0.11.5.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Assertions, Version=1.2.11.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Core, Version=1.2.11.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit, Version=1.2.11.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + global::TUnit.Core.SourceRegistrar.RegisterAssembly(() => global::System.Reflection.Assembly.Load("TUnit.Engine, Version=1.2.11.0, Culture=neutral, PublicKeyToken=b8d4030011dbd70c")); + } +} + +``` + + + + +```csharp showLineNumbers +// +#pragma warning disable + +[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "1.0.0.0")] +file static class DisableReflectionScanner_74bd22630f9848fb858269b3a1b085a4 +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::TUnit.Core.SourceRegistrar.IsEnabled = true; + } +} + +``` + + + + +```csharp showLineNumbers +// +#pragma warning disable + +#nullable enable +// No conversion operators found + +``` + + + + +```csharp showLineNumbers +// +#pragma warning disable + +#nullable enable +namespace TUnit.Generated; +internal sealed class TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum_TestSource : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource +{ + public async global::System.Collections.Generic.IAsyncEnumerable GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default) + { + var metadata = new global::TUnit.Core.TestMetadata + { + TestName = "Add_WithTwoNumbers_ReturnsSum", + TestClassType = typeof(global::TestDemo.FirstTest), + TestMethodName = "Add_WithTwoNumbers_ReturnsSum", + Dependencies = global::System.Array.Empty(), + AttributeFactory = static () => + [ + new global::TUnit.Core.TestAttribute(), + new global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v10.0") +{FrameworkDisplayName = ".NET 10.0",}, + new global::System.Reflection.AssemblyCompanyAttribute("TestDemo"), + new global::System.Reflection.AssemblyConfigurationAttribute("Debug"), + new global::System.Reflection.AssemblyFileVersionAttribute("1.0.0.0"), + new global::System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+54e5188c456ecd37fa9b7fa05bc1869fe84e7d42"), + new global::System.Reflection.AssemblyProductAttribute("TestDemo"), + new global::System.Reflection.AssemblyTitleAttribute("TestDemo"), + new global::System.Reflection.AssemblyVersionAttribute("1.0.0.0"), + new global::System.Reflection.AssemblyMetadataAttribute("Microsoft.Testing.Platform.Application", "true") + ], + DataSources = global::System.Array.Empty(), + ClassDataSources = global::System.Array.Empty(), + PropertyDataSources = global::System.Array.Empty(), + PropertyInjections = global::System.Array.Empty(), + InheritanceDepth = 0, + FilePath = @"D:\\gth\\RSCG_Examples\\v2\\rscg_examples\\TUnit\\src\\TestDemo\\TestDemo\\FirstTest.cs", + LineNumber = 6, + MethodMetadata = new global::TUnit.Core.MethodMetadata + { + Type = typeof(global::TestDemo.FirstTest), + TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TestDemo.FirstTest)), + Name = "Add_WithTwoNumbers_ReturnsSum", + GenericTypeCount = 0, + ReturnType = typeof(global::System.Threading.Tasks.Task), + ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)), + Parameters = global::System.Array.Empty(), + Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestDemo:global::TestDemo.FirstTest", static () => + { + var classMetadata = new global::TUnit.Core.ClassMetadata + { + Type = typeof(global::TestDemo.FirstTest), + TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TestDemo.FirstTest)), + Name = "FirstTest", + Namespace = "TestDemo", + Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestDemo", static () => new global::TUnit.Core.AssemblyMetadata \{ Name = "TestDemo" }), + Parameters = global::System.Array.Empty(), + Properties = global::System.Array.Empty(), + Parent = null + }; + foreach (var prop in classMetadata.Properties) + { + prop.ClassMetadata = classMetadata; + prop.ContainingTypeMetadata = classMetadata; + } + return classMetadata; + }) + }, + InstanceFactory = (typeArgs, args) => new global::TestDemo.FirstTest(), + InvokeTypedTest = static (instance, args, cancellationToken) => + { + try + { + return new global::System.Threading.Tasks.ValueTask(instance.Add_WithTwoNumbers_ReturnsSum()); + } + catch (global::System.Exception ex) + { + return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex)); + } + }, + }; + metadata.UseRuntimeDataGeneration(testSessionId); + yield return metadata; + yield break; + } +} +internal static class TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum_ModuleInitializer +{ + [global::System.Runtime.CompilerServices.ModuleInitializer] + public static void Initialize() + { + global::TUnit.Core.SourceRegistrar.Register(typeof(global::TestDemo.FirstTest), new TestDemo_FirstTest_Add_WithTwoNumbers_ReturnsSum_TestSource()); + } +} + +``` + + + + +## Useful + +### Download Example (.NET C#) + +:::tip + +[Download Example project TUnit ](/sources/TUnit.zip) + +::: + + +### Share TUnit + + + +https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit + + + diff --git a/v2/rscg_examples_site/docs/RSCG-Examples/index.md b/v2/rscg_examples_site/docs/RSCG-Examples/index.md index e9b9864b4..7c4f71bf5 100644 --- a/v2/rscg_examples_site/docs/RSCG-Examples/index.md +++ b/v2/rscg_examples_site/docs/RSCG-Examples/index.md @@ -1,7 +1,7 @@ --- sidebar_position: 30 -title: 238 RSCG list by category -description: 238 RSCG list by category +title: 239 RSCG list by category +description: 239 RSCG list by category slug: /rscg-examples --- @@ -1417,7 +1417,7 @@ import DocCardList from '@theme/DocCardList'; ## Tests
- Expand Tests =>examples:5 + Expand Tests =>examples:6 @@ -1443,6 +1443,11 @@ import DocCardList from '@theme/DocCardList'; [MockMe](/docs/MockMe) + + + +[TUnit](/docs/TUnit) +
@@ -1953,6 +1958,8 @@ flowchart LR; Tests--> MockMe((MockMe)) + Tests--> TUnit((TUnit)) + Validator--> validly((validly)) WinAPI--> Com((Com)) diff --git a/v2/rscg_examples_site/docs/about.md b/v2/rscg_examples_site/docs/about.md index 417e8a642..979f8c7ea 100644 --- a/v2/rscg_examples_site/docs/about.md +++ b/v2/rscg_examples_site/docs/about.md @@ -6,7 +6,7 @@ title: About ## Content You will find here code examples -of 238 Roslyn Source Code Generator (RSCG) +of 239 Roslyn Source Code Generator (RSCG) that can be useful for you. That means, you will write more elegant and concise code - even if the generators code is not always nice to look. ## Are those examples ready for production? diff --git a/v2/rscg_examples_site/docs/indexRSCG.md b/v2/rscg_examples_site/docs/indexRSCG.md index f87492bb4..1a9fa9a6a 100644 --- a/v2/rscg_examples_site/docs/indexRSCG.md +++ b/v2/rscg_examples_site/docs/indexRSCG.md @@ -7,9 +7,9 @@ slug: /List-of-RSCG import useBaseUrl from '@docusaurus/useBaseUrl'; -## 238 RSCG with examples in descending chronological order +## 239 RSCG with examples in descending chronological order -This is the list of 238 ( 15 from Microsoft) RSCG with examples +This is the list of 239 ( 15 from Microsoft) RSCG with examples [See by category](/docs/rscg-examples) [See as json](/exports/RSCG.json) [See as Excel](/exports/RSCG.xlsx) @@ -20,6 +20,7 @@ This is the list of 238 ( 15 from Microsoft) RSCG with examples | No | Name | Date | Category | | --------- | ----- | ---- | -------- | +|239| [TUnit by Tom Longhurst ](/docs/TUnit)|2025-11-08 => 08 November 2025 | [Tests](/docs/Categories/Tests) | |238| [TeCLI by Tyler Coles ](/docs/TeCLI)|2025-11-07 => 07 November 2025 | [CommandLine](/docs/Categories/CommandLine) | |237| [Program by Microsoft ](/docs/Program)|2025-11-06 => 06 November 2025 | [EnhancementClass](/docs/Categories/EnhancementClass) | |236| [validly by Roman Jambor ](/docs/validly)|2025-10-06 => 06 October 2025 | [Validator](/docs/Categories/Validator) | diff --git a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js index 1a4764f53..ddfdd112e 100644 --- a/v2/rscg_examples_site/src/components/HomepageFeatures/index.js +++ b/v2/rscg_examples_site/src/components/HomepageFeatures/index.js @@ -4,7 +4,7 @@ import styles from './styles.module.css'; const FeatureList = [ { -title: '238 Examples (15 from MSFT)', +title: '239 Examples (15 from MSFT)', Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, description: ( <> diff --git a/v2/rscg_examples_site/static/exports/RSCG.json b/v2/rscg_examples_site/static/exports/RSCG.json index 7a0d3696b..369ad5e13 100644 --- a/v2/rscg_examples_site/static/exports/RSCG.json +++ b/v2/rscg_examples_site/static/exports/RSCG.json @@ -1905,6 +1905,14 @@ "Source": "https://github.com/tyevco/TeCLI", "Category": "CommandLine", "AddedOn": "2025-11-07T00:00:00" + }, + { + "Name": "TUnit", + "Link": "https://ignatandrei.github.io/RSCG_Examples/v2/docs/TUnit", + "NuGet": "https://www.nuget.org/packages/TUnit/", + "Source": "https://github.com/thomhurst/TUnit", + "Category": "Tests", + "AddedOn": "2025-11-08T00:00:00" } ] } \ No newline at end of file diff --git a/v2/rscg_examples_site/static/exports/RSCG.xlsx b/v2/rscg_examples_site/static/exports/RSCG.xlsx index 7762264ad..b34e36bac 100644 Binary files a/v2/rscg_examples_site/static/exports/RSCG.xlsx and b/v2/rscg_examples_site/static/exports/RSCG.xlsx differ diff --git a/v2/rscg_examples_site/static/sources/TUnit.zip b/v2/rscg_examples_site/static/sources/TUnit.zip new file mode 100644 index 000000000..7d6b468f8 Binary files /dev/null and b/v2/rscg_examples_site/static/sources/TUnit.zip differ