From 170da1b3ad39a036d2cd086ae98d2df15dd517c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 09:34:12 +0000 Subject: [PATCH 1/5] feat: add -f/--framework support to TUnit template with .NET Framework handling --- TUnit.Templates.Tests/BasicTemplateTests.cs | 6 ++ .../TUnit/BasicTests.cs | 53 +++++++++++++ .../TUnit/Calculator.cs | 10 +++ .../TUnit/Data/AdditionDataGenerator.cs | 12 +++ .../TUnit/Data/InMemoryDb.cs | 34 ++++++++ .../TUnit/DataDrivenTests.cs | 77 +++++++++++++++++++ .../TUnit/DependencyInjectionTests.cs | 47 +++++++++++ .../TUnit/HooksAndLifecycle.cs | 23 ++++++ .../ExcludeFromCodeCoverageAttribute.cs | 1 + .../TUnit/TUnit.csproj | 18 +++++ TUnit.Templates.Tests/TemplateTestBase.cs | 12 +++ .../TUnit/.template.config/template.json | 36 ++++++++- .../ExcludeFromCodeCoverageAttribute.cs | 9 +++ .../content/TUnit/TestProject.csproj | 8 ++ 14 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/BasicTests.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Calculator.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/AdditionDataGenerator.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/InMemoryDb.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DataDrivenTests.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DependencyInjectionTests.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs create mode 100644 TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj create mode 100644 TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs diff --git a/TUnit.Templates.Tests/BasicTemplateTests.cs b/TUnit.Templates.Tests/BasicTemplateTests.cs index 4f83bee5343..7ebf880ad4b 100644 --- a/TUnit.Templates.Tests/BasicTemplateTests.cs +++ b/TUnit.Templates.Tests/BasicTemplateTests.cs @@ -10,6 +10,12 @@ public async Task InstantiationTest() await Engine.Execute(Options).ConfigureAwait(false); } + [Test] + public async Task InstantiationTestWithNetFramework() + { + await Engine.Execute(OptionsWithFramework("net48")).ConfigureAwait(false); + } + [Test] public async Task InstantiationTestWithFSharp() { diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/BasicTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/BasicTests.cs new file mode 100644 index 00000000000..6ad58481c5d --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/BasicTests.cs @@ -0,0 +1,53 @@ +namespace TUnit; + +public class BasicTests +{ + [Before(Class)] + public static Task BeforeClass(ClassHookContext context) + { + // Runs once before all tests in this class + return Task.CompletedTask; + } + + [After(Class)] + public static Task AfterClass(ClassHookContext context) + { + // Runs once after all tests in this class + return Task.CompletedTask; + } + + [Before(Test)] + public Task BeforeTest(TestContext context) + { + // Runs before each test in this class + return Task.CompletedTask; + } + + [After(Test)] + public Task AfterTest(TestContext context) + { + // Runs after each test in this class + return Task.CompletedTask; + } + + [Test] + public async Task Add_ReturnsSum() + { + var calculator = new Calculator(); + + var result = calculator.Add(1, 2); + + await Assert.That(result).IsEqualTo(3); + } + + [Test] + public async Task Divide_ByZero_ThrowsException() + { + var calculator = new Calculator(); + + var action = () => calculator.Divide(1, 0); + + await Assert.That(action).ThrowsException() + .WithMessage("Attempted to divide by zero."); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Calculator.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Calculator.cs new file mode 100644 index 00000000000..323cfbcd8e1 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Calculator.cs @@ -0,0 +1,10 @@ +namespace TUnit; + +public class Calculator +{ + public int Add(int a, int b) => a + b; + public int Subtract(int a, int b) => a - b; + public int Multiply(int a, int b) => a * b; + public double Divide(int a, int b) => + b == 0 ? throw new DivideByZeroException() : (double)a / b; +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/AdditionDataGenerator.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/AdditionDataGenerator.cs new file mode 100644 index 00000000000..6d375a29138 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/AdditionDataGenerator.cs @@ -0,0 +1,12 @@ +namespace TUnit; + +public class AdditionDataGeneratorAttribute : DataSourceGeneratorAttribute +{ + protected override IEnumerable> GenerateDataSources( + DataGeneratorMetadata dataGeneratorMetadata) + { + yield return () => (1, 1, 2); + yield return () => (4, 5, 9); + yield return () => (-1, 1, 0); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/InMemoryDb.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/InMemoryDb.cs new file mode 100644 index 00000000000..a6a674aa427 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Data/InMemoryDb.cs @@ -0,0 +1,34 @@ +using TUnit.Core.Interfaces; + +namespace TUnit; + +public class InMemoryDb : IAsyncInitializer, IAsyncDisposable +{ + private Dictionary _store = null!; + + public Task InitializeAsync() + { + // Simulate async setup - e.g. connecting to a database or starting a container + _store = new Dictionary(); + return Task.CompletedTask; + } + + public Task SetAsync(string key, string value) + { + _store[key] = value; + return Task.CompletedTask; + } + + public Task GetAsync(string key) + { + _store.TryGetValue(key, out var value); + return Task.FromResult(value); + } + + public ValueTask DisposeAsync() + { + // Simulate async teardown - e.g. closing connections, removing containers + _store.Clear(); + return ValueTask.CompletedTask; + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DataDrivenTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DataDrivenTests.cs new file mode 100644 index 00000000000..1a73b35edf2 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DataDrivenTests.cs @@ -0,0 +1,77 @@ +namespace TUnit; + +public class DataDrivenTests +{ + [Test] + [Arguments(1, 2, 3)] + [Arguments(5, -3, 2)] + [Arguments(0, 0, 0)] + public async Task Add_WithArguments(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Add(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [MethodDataSource(nameof(SubtractCases))] + public async Task Subtract_WithMethodDataSource(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Subtract(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [AdditionDataGenerator] + public async Task Add_WithCustomDataGenerator(int a, int b, int expected) + { + var calculator = new Calculator(); + + var result = calculator.Add(a, b); + + await Assert.That(result).IsEqualTo(expected); + } + + [Test] + [MatrixDataSource] + public async Task Multiply_AllCombinations( + [Matrix(1, 2, 3)] int a, + [Matrix(0, 1, -1)] int b) + { + var calculator = new Calculator(); + + var result = calculator.Multiply(a, b); + + await Assert.That(result).IsEqualTo(a * b); + } + + public static IEnumerable<(int, int, int)> SubtractCases() + { + yield return (5, 3, 2); + yield return (10, 7, 3); + yield return (0, 0, 0); + } +} + +// Arguments can also be applied at the class level to parameterize the constructor +[Arguments(10)] +[Arguments(100)] +public class ClassLevelArgumentTests(int divisor) +{ + [Test] + [Arguments(100)] + [Arguments(50)] + public async Task Divide_WithClassAndMethodArguments(int dividend) + { + var calculator = new Calculator(); + + var result = calculator.Divide(dividend, divisor); + + await Assert.That(result).IsGreaterThan(0); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DependencyInjectionTests.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DependencyInjectionTests.cs new file mode 100644 index 00000000000..ff40313c0ec --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/DependencyInjectionTests.cs @@ -0,0 +1,47 @@ +namespace TUnit; + +public class DependencyInjectionTests +{ + // A new InMemoryDb is created for each test + [Test] + [ClassDataSource] + public async Task Database_SetAndGet(InMemoryDb db) + { + await db.SetAsync("key", "value"); + + var result = await db.GetAsync("key"); + + await Assert.That(result).IsEqualTo("value"); + } + + // The same InMemoryDb instance is shared across all tests in this class + [Test] + [ClassDataSource(Shared = SharedType.PerClass)] + public async Task Database_SharedPerClass(InMemoryDb db) + { + await db.SetAsync("shared", "data"); + + var result = await db.GetAsync("shared"); + + await Assert.That(result).IsNotNull(); + } +} + +// ClassDataSource can also be applied at the class level +[ClassDataSource(Shared = SharedType.PerTestSession)] +public class SharedDatabaseTests(InMemoryDb db) +{ + // Or injected as a property instead of a constructor parameter + [ClassDataSource] + public required Calculator Calculator { get; init; } + + [Test] + public async Task Calculator_ResultCanBeStored() + { + var result = Calculator.Add(2, 3); + + await db.SetAsync("result", result.ToString()); + + await Assert.That(await db.GetAsync("result")).IsEqualTo("5"); + } +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs new file mode 100644 index 00000000000..127591a93a5 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs @@ -0,0 +1,23 @@ +using System.Diagnostics.CodeAnalysis; + +[assembly: ExcludeFromCodeCoverage] + +namespace TUnit; + +public class GlobalHooks +{ + [Before(TestSession)] + public static Task BeforeTestSession(TestSessionContext context) + { + // Runs once before all tests - e.g. start a test container, seed a database + return Task.CompletedTask; + } + + [After(TestSession)] + public static Task AfterTestSession(TestSessionContext context) + { + // Runs once after all tests - e.g. stop containers, clean up resources + return Task.CompletedTask; + } + +} diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs new file mode 100644 index 00000000000..5f282702bb0 --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj new file mode 100644 index 00000000000..72fa049f85f --- /dev/null +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj @@ -0,0 +1,18 @@ + + + + enable + enable + Exe + net48 + preview + false + $(NoWarn);CS0592;CS0436 + + + + + + + + diff --git a/TUnit.Templates.Tests/TemplateTestBase.cs b/TUnit.Templates.Tests/TemplateTestBase.cs index b55be5708b0..fbfd1ab1d17 100644 --- a/TUnit.Templates.Tests/TemplateTestBase.cs +++ b/TUnit.Templates.Tests/TemplateTestBase.cs @@ -34,6 +34,18 @@ public abstract partial class TemplateTestBase : IDisposable .AddScrubber(ScrubVersions, "vbproj") ); + protected TemplateVerifierOptions OptionsWithFramework(string framework) => + new TemplateVerifierOptions(TemplateShortName) + { + TemplatePath = Path.Combine(TestContext.OutputDirectory!, "content", TemplateShortName), + TemplateSpecificArgs = ["--framework", framework], + }.WithCustomScrubbers( + ScrubbersDefinition.Empty + .AddScrubber(ScrubVersions, "csproj") + .AddScrubber(ScrubVersions, "fsproj") + .AddScrubber(ScrubVersions, "vbproj") + ); + private const string ScrubbedVersion = "0.0.0-scrubbed"; private static void ScrubVersions(StringBuilder sb) diff --git a/TUnit.Templates/content/TUnit/.template.config/template.json b/TUnit.Templates/content/TUnit/.template.config/template.json index a2c1976f9a8..3215c63b8d8 100644 --- a/TUnit.Templates/content/TUnit/.template.config/template.json +++ b/TUnit.Templates/content/TUnit/.template.config/template.json @@ -15,6 +15,40 @@ "type": "project" }, "sourceName": "TestProject", - "preferNameDirectory": true + "preferNameDirectory": true, + "symbols": { + "framework": { + "type": "parameter", + "description": "The target framework for the project.", + "datatype": "choice", + "choices": [ + { "choice": "net10.0", "description": "Target .NET 10.0" }, + { "choice": "net9.0", "description": "Target .NET 9.0" }, + { "choice": "net8.0", "description": "Target .NET 8.0" }, + { "choice": "net481", "description": "Target .NET Framework 4.8.1" }, + { "choice": "net48", "description": "Target .NET Framework 4.8" }, + { "choice": "net472", "description": "Target .NET Framework 4.7.2" }, + { "choice": "net462", "description": "Target .NET Framework 4.6.2" } + ], + "defaultValue": "net10.0", + "replaces": "net10.0" + }, + "IsNetFramework": { + "type": "computed", + "value": "(framework == \"net462\" || framework == \"net472\" || framework == \"net48\" || framework == \"net481\")" + } + }, + "sources": [ + { + "modifiers": [ + { + "condition": "(!IsNetFramework)", + "exclude": [ + "Polyfills/**" + ] + } + ] + } + ] } \ No newline at end of file diff --git a/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs new file mode 100644 index 00000000000..1eb8e21d6ad --- /dev/null +++ b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -0,0 +1,9 @@ +#if NETFRAMEWORK +namespace System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] +internal sealed class ExcludeFromCodeCoverageAttribute : Attribute +{ + public string? Justification { get; set; } +} +#endif diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index c564cd0e5a6..727eac8e6ea 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -5,10 +5,18 @@ enable Exe net10.0 + + preview + false + $(NoWarn);CS0592;CS0436 + + + + From 190c8dbeeee91e45dd0db600381221f12e99a30c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Jun 2026 09:36:09 +0000 Subject: [PATCH 2/5] fix: remove #if NETFRAMEWORK guard from polyfill (template engine processes it) --- .../TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs | 8 +++++++- .../TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs index 5f282702bb0..7fc179e08c0 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -1 +1,7 @@ - \ No newline at end of file +namespace System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] +internal sealed class ExcludeFromCodeCoverageAttribute : Attribute +{ + public string? Justification { get; set; } +} diff --git a/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs index 1eb8e21d6ad..7fc179e08c0 100644 --- a/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs +++ b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -1,4 +1,3 @@ -#if NETFRAMEWORK namespace System.Diagnostics.CodeAnalysis; [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] @@ -6,4 +5,3 @@ internal sealed class ExcludeFromCodeCoverageAttribute : Attribute { public string? Justification { get; set; } } -#endif From 49ece7fc963ffbd299661204efb4c39e4ac78226 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 12 Jul 2026 16:21:13 +0100 Subject: [PATCH 3/5] fix(templates): make global hooks static --- .../TUnit/HooksAndLifecycle.cs | 2 +- .../TUnit/HooksAndLifecycle.cs | 2 +- TUnit.Templates/content/TUnit/HooksAndLifecycle.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs index 64447d5af62..64dc762e428 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTest.TUnit._.verified/TUnit/HooksAndLifecycle.cs @@ -4,7 +4,7 @@ namespace TUnit; -public class GlobalHooks +public static class GlobalHooks { [Before(TestSession)] public static Task BeforeTestSession(TestSessionContext context) diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs index 127591a93a5..e06ea708277 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs @@ -4,7 +4,7 @@ namespace TUnit; -public class GlobalHooks +public static class GlobalHooks { [Before(TestSession)] public static Task BeforeTestSession(TestSessionContext context) diff --git a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs index a47c4182ad9..af3e8df1945 100644 --- a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs @@ -4,7 +4,7 @@ namespace TestProject; -public class GlobalHooks +public static class GlobalHooks { [Before(TestSession)] public static Task BeforeTestSession(TestSessionContext context) From b5961288912cd825195700a36418f75388a77bb7 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:54:14 +0100 Subject: [PATCH 4/5] fix(templates): scope netfx coverage shim --- .../TUnit/HooksAndLifecycle.cs | 2 +- .../TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs | 2 +- .../TUnit/TUnit.csproj | 1 - TUnit.Templates/content/TUnit/HooksAndLifecycle.cs | 4 ++++ .../TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs | 2 +- TUnit.Templates/content/TUnit/TestProject.csproj | 3 +-- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs index e06ea708277..e50efd8dfe5 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -[assembly: ExcludeFromCodeCoverage] +[assembly: TUnit.Polyfills.ExcludeFromCodeCoverage] namespace TUnit; diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs index 7fc179e08c0..1ecef0281bd 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -1,4 +1,4 @@ -namespace System.Diagnostics.CodeAnalysis; +namespace TUnit.Polyfills; [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] internal sealed class ExcludeFromCodeCoverageAttribute : Attribute diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj index 72fa049f85f..a6faca00d2f 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/TUnit.csproj @@ -7,7 +7,6 @@ net48 preview false - $(NoWarn);CS0592;CS0436 diff --git a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs index af3e8df1945..6a32964fc7e 100644 --- a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs @@ -1,6 +1,10 @@ using System.Diagnostics.CodeAnalysis; +#if (IsNetFramework) +[assembly: TestProject.Polyfills.ExcludeFromCodeCoverage] +#else [assembly: ExcludeFromCodeCoverage] +#endif namespace TestProject; diff --git a/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs index 7fc179e08c0..89b039fb63b 100644 --- a/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs +++ b/TUnit.Templates/content/TUnit/Polyfills/ExcludeFromCodeCoverageAttribute.cs @@ -1,4 +1,4 @@ -namespace System.Diagnostics.CodeAnalysis; +namespace TestProject.Polyfills; [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, Inherited = false, AllowMultiple = false)] internal sealed class ExcludeFromCodeCoverageAttribute : Attribute diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj index 727eac8e6ea..8f279359f7e 100644 --- a/TUnit.Templates/content/TUnit/TestProject.csproj +++ b/TUnit.Templates/content/TUnit/TestProject.csproj @@ -8,14 +8,13 @@ preview false - $(NoWarn);CS0592;CS0436 - + From 6125abefc90fbbc90c07d63b7c930d99fc780baa Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Sun, 12 Jul 2026 23:54:56 +0100 Subject: [PATCH 5/5] fix(templates): remove unused netfx using --- .../TUnit/HooksAndLifecycle.cs | 3 +-- TUnit.Templates/content/TUnit/HooksAndLifecycle.cs | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs index e50efd8dfe5..c3eefa0ec66 100644 --- a/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates.Tests/Snapshots/InstantiationTestWithNetFramework.TUnit.--framework#net48.verified/TUnit/HooksAndLifecycle.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; - + [assembly: TUnit.Polyfills.ExcludeFromCodeCoverage] namespace TUnit; diff --git a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs index 6a32964fc7e..c20e761dcbb 100644 --- a/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs +++ b/TUnit.Templates/content/TUnit/HooksAndLifecycle.cs @@ -1,4 +1,6 @@ +#if (!IsNetFramework) using System.Diagnostics.CodeAnalysis; +#endif #if (IsNetFramework) [assembly: TestProject.Polyfills.ExcludeFromCodeCoverage]