diff --git a/Directory.Packages.props b/Directory.Packages.props
index f7ebc259bd..cc33abe666 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -59,7 +59,7 @@
-
+
@@ -83,9 +83,9 @@
-
-
-
+
+
+
diff --git a/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
index 8cb0b1debb..1621a2f664 100644
--- a/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
+++ b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
@@ -226,6 +226,22 @@ private static void GenerateExtensionMethod(
// Skip the first parameter (AssertionContext)
var additionalParams = constructor.Parameters.Skip(1).ToArray();
+ // Check for RequiresUnreferencedCode attribute on the constructor first, then fall back to class-level
+ var constructorRequiresUnreferencedCodeAttr = constructor.GetAttributes()
+ .FirstOrDefault(attr => attr.AttributeClass?.Name == "RequiresUnreferencedCodeAttribute");
+
+ string? requiresUnreferencedCodeMessage = null;
+ if (constructorRequiresUnreferencedCodeAttr != null && constructorRequiresUnreferencedCodeAttr.ConstructorArguments.Length > 0)
+ {
+ // Constructor-level attribute takes precedence
+ requiresUnreferencedCodeMessage = constructorRequiresUnreferencedCodeAttr.ConstructorArguments[0].Value?.ToString();
+ }
+ else if (!string.IsNullOrEmpty(data.RequiresUnreferencedCodeMessage))
+ {
+ // Fall back to class-level attribute
+ requiresUnreferencedCodeMessage = data.RequiresUnreferencedCodeMessage;
+ }
+
// Build generic type parameters string
// Use the assertion class's own type parameters if it has them
var genericParams = new List();
@@ -315,10 +331,10 @@ private static void GenerateExtensionMethod(
sourceBuilder.AppendLine($" /// Extension method for {assertionType.Name}.");
sourceBuilder.AppendLine(" /// ");
- // Add RequiresUnreferencedCode attribute if present
- if (!string.IsNullOrEmpty(data.RequiresUnreferencedCodeMessage))
+ // Add RequiresUnreferencedCode attribute if present (from constructor or class level)
+ if (!string.IsNullOrEmpty(requiresUnreferencedCodeMessage))
{
- var escapedMessage = data.RequiresUnreferencedCodeMessage!.Replace("\"", "\\\"");
+ var escapedMessage = requiresUnreferencedCodeMessage!.Replace("\"", "\\\"");
sourceBuilder.AppendLine($" [global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(\"{escapedMessage}\")]");
}
diff --git a/TUnit.Assertions/Conditions/CollectionComparerBasedAssertion.cs b/TUnit.Assertions/Conditions/CollectionComparerBasedAssertion.cs
index 5a2f78fec1..cd168e5bba 100644
--- a/TUnit.Assertions/Conditions/CollectionComparerBasedAssertion.cs
+++ b/TUnit.Assertions/Conditions/CollectionComparerBasedAssertion.cs
@@ -1,4 +1,3 @@
-using System.Collections;
using TUnit.Assertions.Core;
using TUnit.Assertions.Sources;
@@ -13,7 +12,7 @@ namespace TUnit.Assertions.Conditions;
public abstract class CollectionComparerBasedAssertion : CollectionAssertionBase
where TCollection : IEnumerable
{
- private IEqualityComparer? _comparer;
+ protected IEqualityComparer? Comparer;
protected CollectionComparerBasedAssertion(AssertionContext context)
: base(context)
@@ -26,7 +25,7 @@ protected CollectionComparerBasedAssertion(AssertionContext context
///
protected void SetComparer(IEqualityComparer comparer)
{
- _comparer = comparer;
+ Comparer = comparer;
Context.ExpressionBuilder.Append($".Using({comparer.GetType().Name})");
}
@@ -36,14 +35,6 @@ protected void SetComparer(IEqualityComparer comparer)
///
protected IEqualityComparer GetComparer()
{
- return _comparer ?? EqualityComparer.Default;
- }
-
- ///
- /// Checks if a custom comparer has been specified.
- ///
- protected bool HasCustomComparer()
- {
- return _comparer != null;
+ return Comparer ?? EqualityComparer.Default;
}
}
diff --git a/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs b/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
index 6eaf69bf8b..7592c54f0e 100644
--- a/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
+++ b/TUnit.Assertions/Conditions/IsEquivalentToAssertion.cs
@@ -14,21 +14,19 @@ namespace TUnit.Assertions.Conditions;
/// Inherits from CollectionComparerBasedAssertion to preserve collection type awareness in And/Or chains.
///
[AssertionExtension("IsEquivalentTo")]
-[RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public class IsEquivalentToAssertion : CollectionComparerBasedAssertion
where TCollection : IEnumerable
{
private readonly IEnumerable _expected;
private readonly CollectionOrdering _ordering;
+ [RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public IsEquivalentToAssertion(
AssertionContext context,
IEnumerable expected,
CollectionOrdering ordering = CollectionOrdering.Any)
- : base(context)
+ : this(context, expected, StructuralEqualityComparer.Instance, ordering)
{
- _expected = expected ?? throw new ArgumentNullException(nameof(expected));
- _ordering = ordering;
}
public IsEquivalentToAssertion(
@@ -40,7 +38,7 @@ public IsEquivalentToAssertion(
{
_expected = expected ?? throw new ArgumentNullException(nameof(expected));
_ordering = ordering;
- SetComparer(comparer);
+ Comparer = comparer;
}
public IsEquivalentToAssertion Using(IEqualityComparer comparer)
@@ -49,7 +47,6 @@ public IsEquivalentToAssertion Using(IEqualityComparer CheckAsync(EvaluationMetadata metadata)
{
var value = metadata.Value;
@@ -60,7 +57,7 @@ protected override Task CheckAsync(EvaluationMetadata.Instance;
+ var comparer = GetComparer();
var result = CollectionEquivalencyChecker.AreEquivalent(
value,
diff --git a/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs b/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
index 5fbb18d0d7..e32548337f 100644
--- a/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
+++ b/TUnit.Assertions/Conditions/NotEquivalentToAssertion.cs
@@ -13,21 +13,31 @@ namespace TUnit.Assertions.Conditions;
/// Inherits from CollectionComparerBasedAssertion to preserve collection type awareness in And/Or chains.
///
[AssertionExtension("IsNotEquivalentTo")]
-[RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public class NotEquivalentToAssertion : CollectionComparerBasedAssertion
where TCollection : IEnumerable
{
private readonly IEnumerable _notExpected;
private readonly CollectionOrdering _ordering;
+ [RequiresUnreferencedCode("Collection equivalency uses structural comparison for complex objects, which requires reflection and is not compatible with AOT")]
public NotEquivalentToAssertion(
AssertionContext context,
IEnumerable notExpected,
CollectionOrdering ordering = CollectionOrdering.Any)
+ : this(context, notExpected, StructuralEqualityComparer.Instance, ordering)
+ {
+ }
+
+ public NotEquivalentToAssertion(
+ AssertionContext context,
+ IEnumerable notExpected,
+ IEqualityComparer comparer,
+ CollectionOrdering ordering = CollectionOrdering.Any)
: base(context)
{
_notExpected = notExpected ?? throw new ArgumentNullException(nameof(notExpected));
_ordering = ordering;
+ Comparer = comparer;
}
public NotEquivalentToAssertion Using(IEqualityComparer comparer)
@@ -36,7 +46,6 @@ public NotEquivalentToAssertion Using(IEqualityComparer CheckAsync(EvaluationMetadata metadata)
{
var value = metadata.Value;
@@ -47,7 +56,7 @@ protected override Task CheckAsync(EvaluationMetadata.Instance;
+ var comparer = GetComparer();
var result = CollectionEquivalencyChecker.AreEquivalent(
value,
diff --git a/TUnit.Core/TUnit.Core.targets b/TUnit.Core/TUnit.Core.targets
index ae21f778a1..3e87a68017 100644
--- a/TUnit.Core/TUnit.Core.targets
+++ b/TUnit.Core/TUnit.Core.targets
@@ -8,7 +8,7 @@
- <_TUnitPolyfillVersion>9.0.3
+ <_TUnitPolyfillVersion>9.1.0
<_TUnitNeedsPolyfill Condition="'$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFrameworkIdentifier)' == '.NETFramework'">true
diff --git a/TUnit.Engine/Building/TestBuilder.cs b/TUnit.Engine/Building/TestBuilder.cs
index ada56f7737..a854f4e2df 100644
--- a/TUnit.Engine/Building/TestBuilder.cs
+++ b/TUnit.Engine/Building/TestBuilder.cs
@@ -206,7 +206,8 @@ public async Task> BuildTestsFromMetadataAsy
hasAnyClassData = true;
classDataLoopIndex++;
- var classData = DataUnwrapper.Unwrap(await classDataFactory() ?? []);
+ var classDataResult = await classDataFactory() ?? [];
+ var classData = DataUnwrapper.Unwrap(classDataResult);
var needsInstanceForMethodDataSources = metadata.DataSources.Any(ds => ds is IAccessesInstanceData);
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
index 35bcf00a2e..9714184f79 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet10_0.verified.txt
@@ -388,9 +388,9 @@ namespace .Conditions
public abstract class CollectionComparerBasedAssertion : .
where TCollection : .
{
+ protected .? Comparer;
protected CollectionComparerBasedAssertion(. context) { }
protected . GetComparer() { }
- protected bool HasCustomComparer() { }
protected void SetComparer(. comparer) { }
}
[.("Contains")]
@@ -817,15 +817,14 @@ namespace .Conditions
protected override string GetExpectation() { }
public . Using(. comparer) { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsEquivalentTo")]
public class IsEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public IsEquivalentToAssertion(. context, . expected, . ordering = 0) { }
public IsEquivalentToAssertion(. context, . expected, . comparer, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -919,14 +918,14 @@ namespace .Conditions
public . IgnoringType( type) { }
public . IgnoringType() { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsNotEquivalentTo")]
public class NotEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public NotEquivalentToAssertion(. context, . notExpected, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
+ public NotEquivalentToAssertion(. context, . notExpected, . comparer, . ordering = 0) { }
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -3085,8 +3084,6 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . comparer, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
}
@@ -3184,6 +3181,8 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsNotEquivalentTo(this . source, . notExpected, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
+ public static . IsNotEquivalentTo(this . source, . notExpected, . comparer, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
+ where TCollection : . { }
}
public static class NotSameReferenceAssertionExtensions
{
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
index 060668995b..665faeb994 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet8_0.verified.txt
@@ -385,9 +385,9 @@ namespace .Conditions
public abstract class CollectionComparerBasedAssertion : .
where TCollection : .
{
+ protected .? Comparer;
protected CollectionComparerBasedAssertion(. context) { }
protected . GetComparer() { }
- protected bool HasCustomComparer() { }
protected void SetComparer(. comparer) { }
}
[.("Contains")]
@@ -814,15 +814,14 @@ namespace .Conditions
protected override string GetExpectation() { }
public . Using(. comparer) { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsEquivalentTo")]
public class IsEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public IsEquivalentToAssertion(. context, . expected, . ordering = 0) { }
public IsEquivalentToAssertion(. context, . expected, . comparer, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -916,14 +915,14 @@ namespace .Conditions
public . IgnoringType( type) { }
public . IgnoringType() { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsNotEquivalentTo")]
public class NotEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public NotEquivalentToAssertion(. context, . notExpected, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
+ public NotEquivalentToAssertion(. context, . notExpected, . comparer, . ordering = 0) { }
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -3068,8 +3067,6 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . comparer, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
}
@@ -3166,6 +3163,8 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsNotEquivalentTo(this . source, . notExpected, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
+ public static . IsNotEquivalentTo(this . source, . notExpected, . comparer, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
+ where TCollection : . { }
}
public static class NotSameReferenceAssertionExtensions
{
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
index 634cbf0e26..c8665a0b6a 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.DotNet9_0.verified.txt
@@ -388,9 +388,9 @@ namespace .Conditions
public abstract class CollectionComparerBasedAssertion : .
where TCollection : .
{
+ protected .? Comparer;
protected CollectionComparerBasedAssertion(. context) { }
protected . GetComparer() { }
- protected bool HasCustomComparer() { }
protected void SetComparer(. comparer) { }
}
[.("Contains")]
@@ -817,15 +817,14 @@ namespace .Conditions
protected override string GetExpectation() { }
public . Using(. comparer) { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsEquivalentTo")]
public class IsEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public IsEquivalentToAssertion(. context, . expected, . ordering = 0) { }
public IsEquivalentToAssertion(. context, . expected, . comparer, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -919,14 +918,14 @@ namespace .Conditions
public . IgnoringType( type) { }
public . IgnoringType() { }
}
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
[.("IsNotEquivalentTo")]
public class NotEquivalentToAssertion : .
where TCollection : .
{
+ [.("Collection equivalency uses structural comparison for complex objects, which requ" +
+ "ires reflection and is not compatible with AOT")]
public NotEquivalentToAssertion(. context, . notExpected, . ordering = 0) { }
- [.("AOT", "IL3050", Justification="Collection equivalency uses structural comparison which requires reflection")]
+ public NotEquivalentToAssertion(. context, . notExpected, . comparer, . ordering = 0) { }
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -3085,8 +3084,6 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
- [.("Collection equivalency uses structural comparison for complex objects, which requ" +
- "ires reflection and is not compatible with AOT")]
public static . IsEquivalentTo(this . source, . expected, . comparer, . ordering = 0, [.("expected")] string? expectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
}
@@ -3184,6 +3181,8 @@ namespace .Extensions
"ires reflection and is not compatible with AOT")]
public static . IsNotEquivalentTo(this . source, . notExpected, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
+ public static . IsNotEquivalentTo(this . source, . notExpected, . comparer, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
+ where TCollection : . { }
}
public static class NotSameReferenceAssertionExtensions
{
diff --git a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt
index 03c58549c0..bd832a6fca 100644
--- a/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt
+++ b/TUnit.PublicAPI/Tests.Assertions_Library_Has_No_API_Changes.Net4_7.verified.txt
@@ -381,9 +381,9 @@ namespace .Conditions
public abstract class CollectionComparerBasedAssertion : .
where TCollection : .
{
+ protected .? Comparer;
protected CollectionComparerBasedAssertion(. context) { }
protected . GetComparer() { }
- protected bool HasCustomComparer() { }
protected void SetComparer(. comparer) { }
}
[.("Contains")]
@@ -893,6 +893,7 @@ namespace .Conditions
where TCollection : .
{
public NotEquivalentToAssertion(. context, . notExpected, . ordering = 0) { }
+ public NotEquivalentToAssertion(. context, . notExpected, . comparer, . ordering = 0) { }
protected override .<.> CheckAsync(. metadata) { }
protected override string GetExpectation() { }
public . Using(. comparer) { }
@@ -2881,6 +2882,8 @@ namespace .Extensions
{
public static . IsNotEquivalentTo(this . source, . notExpected, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("ordering")] string? orderingExpression = null)
where TCollection : . { }
+ public static . IsNotEquivalentTo(this . source, . notExpected, . comparer, . ordering = 0, [.("notExpected")] string? notExpectedExpression = null, [.("comparer")] string? comparerExpression = null, [.("ordering")] string? orderingExpression = null)
+ where TCollection : . { }
}
public static class NotSameReferenceAssertionExtensions
{
diff --git a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj
index 966217ce72..d2e029d0c6 100644
--- a/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj
+++ b/TUnit.Templates/content/TUnit.AspNet.FSharp/TestProject/TestProject.fsproj
@@ -10,8 +10,8 @@
-
-
+
+
diff --git a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj
index d71fa475b1..905a5dd8b7 100644
--- a/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj
+++ b/TUnit.Templates/content/TUnit.AspNet/TestProject/TestProject.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj
index 29270c3a6f..107780bda0 100644
--- a/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj
+++ b/TUnit.Templates/content/TUnit.Aspire.Starter/ExampleNamespace.TestProject/ExampleNamespace.TestProject.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj
index 2750f80878..bbe3ede435 100644
--- a/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj
+++ b/TUnit.Templates/content/TUnit.Aspire.Test/ExampleNamespace.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj
index 77c58677aa..1751db5e8c 100644
--- a/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj
+++ b/TUnit.Templates/content/TUnit.FSharp/TestProject.fsproj
@@ -10,8 +10,8 @@
-
-
+
+
diff --git a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj
index 5e661fc20e..ee11fb3565 100644
--- a/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj
+++ b/TUnit.Templates/content/TUnit.Playwright/TestProject.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj
index 16d50df436..548b07f51c 100644
--- a/TUnit.Templates/content/TUnit.VB/TestProject.vbproj
+++ b/TUnit.Templates/content/TUnit.VB/TestProject.vbproj
@@ -8,6 +8,6 @@
-
+
diff --git a/TUnit.Templates/content/TUnit/TestProject.csproj b/TUnit.Templates/content/TUnit/TestProject.csproj
index f7c72ad4f5..b49bdc3b89 100644
--- a/TUnit.Templates/content/TUnit/TestProject.csproj
+++ b/TUnit.Templates/content/TUnit/TestProject.csproj
@@ -8,7 +8,7 @@
-
+
\ No newline at end of file
diff --git a/TUnit.TestProject/ClassDataSourceSharedNoneRegressionTests.cs b/TUnit.TestProject/ClassDataSourceSharedNoneRegressionTests.cs
new file mode 100644
index 0000000000..15df0b5c36
--- /dev/null
+++ b/TUnit.TestProject/ClassDataSourceSharedNoneRegressionTests.cs
@@ -0,0 +1,156 @@
+using TUnit.Core.Interfaces;
+using TUnit.TestProject.Attributes;
+
+namespace TUnit.TestProject;
+
+///
+/// Regression tests for GitHub Issue #3855
+/// Verifies that ClassDataSource with SharedType.None constructs and initializes objects only once per test.
+///
+[EngineTest(ExpectedResult.Pass)]
+public class ClassDataSourceSharedNoneRegressionTests
+{
+ // Test helper class that tracks construction
+ public class ConstructionCounterClass
+ {
+ private static int _instanceCounter = 0;
+ public int InstanceNumber { get; }
+
+ public ConstructionCounterClass()
+ {
+ InstanceNumber = Interlocked.Increment(ref _instanceCounter);
+ }
+
+ public int Value { get; set; } = 42;
+ }
+
+ // Test helper class that tracks initialization
+ public class AsyncInitializerCounterClass : IAsyncInitializer
+ {
+ private static int _initCounter = 0;
+ public int InitNumber { get; private set; } = -1;
+
+ public Task InitializeAsync()
+ {
+ InitNumber = Interlocked.Increment(ref _initCounter);
+ return Task.CompletedTask;
+ }
+
+ public int Value { get; set; } = 99;
+ }
+
+ ///
+ /// Test that SharedType.None constructs the class data source exactly once per test.
+ /// Regression test for issue #3855 where it was being constructed twice.
+ /// Before the fix, InstanceNumber would be 2 (constructed twice).
+ /// After the fix, InstanceNumber should be 1 (constructed once).
+ ///
+ [Test]
+ [ClassDataSource(Shared = SharedType.None)]
+ public async Task SharedTypeNone_ConstructsOnlyOnce(ConstructionCounterClass instance)
+ {
+ // Instance should be constructed exactly once, so InstanceNumber should be 1
+ // (not 2, which would indicate double construction)
+ await Assert.That(instance).IsNotNull();
+ await Assert.That(instance.InstanceNumber).IsEqualTo(1);
+ await Assert.That(instance.Value).IsEqualTo(42);
+ }
+
+ ///
+ /// Test that SharedType.None with IAsyncInitializer initializes exactly once per test.
+ /// Regression test for issue #3855 where InitializeAsync was being called twice.
+ /// Before the fix, InitNumber would be 2 (initialized twice).
+ /// After the fix, InitNumber should be 1 (initialized once).
+ ///
+ [Test]
+ [ClassDataSource(Shared = SharedType.None)]
+ public async Task SharedTypeNone_WithAsyncInitializer_InitializesOnlyOnce(AsyncInitializerCounterClass instance)
+ {
+ // Instance should be initialized exactly once, so InitNumber should be 1
+ // (not 2, which would indicate double initialization)
+ await Assert.That(instance).IsNotNull();
+ await Assert.That(instance.InitNumber).IsEqualTo(1);
+ await Assert.That(instance.Value).IsEqualTo(99);
+ }
+}
+
+///
+/// Regression tests for GitHub Issue #3855 - Verify no instance leakage across tests.
+/// Tests that multiple test methods in the same class each receive their own unique instance
+/// with SharedType.None (no sharing or leakage across tests).
+///
+public class ClassDataSourceSharedNoneNoLeakageTests
+{
+ // Test helper class that tracks instance IDs and mutation
+ public class UniqueInstanceClass
+ {
+ private static int _nextId = 0;
+ public int InstanceId { get; }
+ public int MutationValue { get; set; }
+
+ public UniqueInstanceClass()
+ {
+ InstanceId = Interlocked.Increment(ref _nextId);
+ MutationValue = 0; // Initial value
+ }
+
+ public static void ResetIdCounter()
+ {
+ _nextId = 0;
+ }
+ }
+
+ [Before(TestSession)]
+ public static void ResetCounters()
+ {
+ UniqueInstanceClass.ResetIdCounter();
+ }
+
+ ///
+ /// First test - should get instance ID 1, mutate it, and not affect other tests
+ ///
+ [Test]
+ [ClassDataSource(Shared = SharedType.None)]
+ public async Task Test1_GetsUniqueInstance(UniqueInstanceClass instance)
+ {
+ // This is the first test, should have InstanceId = 1
+ await Assert.That(instance.InstanceId).IsEqualTo(1);
+ await Assert.That(instance.MutationValue).IsEqualTo(0);
+
+ // Mutate the instance
+ instance.MutationValue = 100;
+ }
+
+ ///
+ /// Second test - should get instance ID 2, with fresh MutationValue = 0
+ /// (proves no leakage from Test1)
+ ///
+ [Test]
+ [ClassDataSource(Shared = SharedType.None)]
+ public async Task Test2_GetsUniqueInstance(UniqueInstanceClass instance)
+ {
+ // This is the second test, should have InstanceId = 2
+ await Assert.That(instance.InstanceId).IsEqualTo(2);
+
+ // MutationValue should be 0 (not affected by Test1's mutation to 100)
+ await Assert.That(instance.MutationValue).IsEqualTo(0);
+
+ // Mutate the instance
+ instance.MutationValue = 200;
+ }
+
+ ///
+ /// Third test - should get instance ID 3, with fresh MutationValue = 0
+ /// (proves no leakage from Test1 or Test2)
+ ///
+ [Test]
+ [ClassDataSource(Shared = SharedType.None)]
+ public async Task Test3_GetsUniqueInstance(UniqueInstanceClass instance)
+ {
+ // This is the third test, should have InstanceId = 3
+ await Assert.That(instance.InstanceId).IsEqualTo(3);
+
+ // MutationValue should be 0 (not affected by Test1 or Test2)
+ await Assert.That(instance.MutationValue).IsEqualTo(0);
+ }
+}
diff --git a/docs/docs/benchmarks/AsyncTests.md b/docs/docs/benchmarks/AsyncTests.md
index 152246dad5..d3f9769eb3 100644
--- a/docs/docs/benchmarks/AsyncTests.md
+++ b/docs/docs/benchmarks/AsyncTests.md
@@ -7,7 +7,7 @@ sidebar_position: 2
# AsyncTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 565.8 ms | 565.3 ms | 3.67 ms |
-| NUnit | 4.4.0 | 647.2 ms | 646.3 ms | 5.71 ms |
-| MSTest | 4.0.2 | 620.4 ms | 618.3 ms | 8.09 ms |
-| xUnit3 | 3.2.0 | 712.3 ms | 712.0 ms | 11.12 ms |
-| **TUnit (AOT)** | 1.2.0 | 125.3 ms | 125.3 ms | 0.28 ms |
+| **TUnit** | 1.2.3 | 556.5 ms | 556.3 ms | 2.03 ms |
+| NUnit | 4.4.0 | 663.3 ms | 662.2 ms | 9.02 ms |
+| MSTest | 4.0.2 | 635.0 ms | 632.0 ms | 11.80 ms |
+| xUnit3 | 3.2.0 | 715.8 ms | 716.7 ms | 8.28 ms |
+| **TUnit (AOT)** | 1.2.3 | 124.5 ms | 124.5 ms | 0.26 ms |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "AsyncTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 855
- bar [565.8, 647.2, 620.4, 712.3, 125.3]
+ y-axis "Time (ms)" 0 --> 859
+ bar [556.5, 663.3, 635, 715.8, 124.5]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.239Z*
+*Last generated: 2025-11-18T00:28:14.218Z*
diff --git a/docs/docs/benchmarks/BuildTime.md b/docs/docs/benchmarks/BuildTime.md
index 20967452e6..d06fcc5888 100644
--- a/docs/docs/benchmarks/BuildTime.md
+++ b/docs/docs/benchmarks/BuildTime.md
@@ -7,7 +7,7 @@ sidebar_position: 8
# Build Performance Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -18,10 +18,10 @@ Compilation time comparison across frameworks:
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 2.003 s | 1.999 s | 0.0153 s |
-| Build_NUnit | 4.4.0 | 1.612 s | 1.610 s | 0.0296 s |
-| Build_MSTest | 4.0.2 | 1.681 s | 1.680 s | 0.0164 s |
-| Build_xUnit3 | 3.2.0 | 1.585 s | 1.582 s | 0.0210 s |
+| **TUnit** | 1.2.3 | 1.947 s | 1.953 s | 0.0381 s |
+| Build_NUnit | 4.4.0 | 1.556 s | 1.557 s | 0.0171 s |
+| Build_MSTest | 4.0.2 | 1.621 s | 1.624 s | 0.0156 s |
+| Build_xUnit3 | 3.2.0 | 1.523 s | 1.527 s | 0.0162 s |
## 📈 Visual Comparison
@@ -60,7 +60,7 @@ xychart-beta
title "Build Time Comparison"
x-axis ["Build_TUnit", "Build_NUnit", "Build_MSTest", "Build_xUnit3"]
y-axis "Time (s)" 0 --> 3
- bar [2.003, 1.612, 1.681, 1.585]
+ bar [1.947, 1.556, 1.621, 1.523]
```
---
@@ -69,4 +69,4 @@ xychart-beta
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.241Z*
+*Last generated: 2025-11-18T00:28:14.220Z*
diff --git a/docs/docs/benchmarks/DataDrivenTests.md b/docs/docs/benchmarks/DataDrivenTests.md
index 5cc4ffb92f..06066bfb23 100644
--- a/docs/docs/benchmarks/DataDrivenTests.md
+++ b/docs/docs/benchmarks/DataDrivenTests.md
@@ -7,7 +7,7 @@ sidebar_position: 3
# DataDrivenTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 472.15 ms | 472.07 ms | 5.427 ms |
-| NUnit | 4.4.0 | 573.40 ms | 571.78 ms | 8.309 ms |
-| MSTest | 4.0.2 | 592.88 ms | 592.10 ms | 9.922 ms |
-| xUnit3 | 3.2.0 | 591.10 ms | 591.00 ms | 10.996 ms |
-| **TUnit (AOT)** | 1.2.0 | 25.00 ms | 25.00 ms | 0.200 ms |
+| **TUnit** | 1.2.3 | 493.26 ms | 492.16 ms | 3.175 ms |
+| NUnit | 4.4.0 | 601.85 ms | 601.22 ms | 13.698 ms |
+| MSTest | 4.0.2 | 617.89 ms | 622.69 ms | 12.013 ms |
+| xUnit3 | 3.2.0 | 610.52 ms | 609.06 ms | 11.016 ms |
+| **TUnit (AOT)** | 1.2.3 | 24.33 ms | 24.31 ms | 0.233 ms |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "DataDrivenTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 712
- bar [472.15, 573.4, 592.88, 591.1, 25]
+ y-axis "Time (ms)" 0 --> 742
+ bar [493.26, 601.85, 617.89, 610.52, 24.33]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.239Z*
+*Last generated: 2025-11-18T00:28:14.218Z*
diff --git a/docs/docs/benchmarks/MassiveParallelTests.md b/docs/docs/benchmarks/MassiveParallelTests.md
index cc4af7feab..5808198b81 100644
--- a/docs/docs/benchmarks/MassiveParallelTests.md
+++ b/docs/docs/benchmarks/MassiveParallelTests.md
@@ -7,7 +7,7 @@ sidebar_position: 4
# MassiveParallelTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 567.7 ms | 568.3 ms | 5.65 ms |
-| NUnit | 4.4.0 | 1,165.6 ms | 1,165.9 ms | 5.31 ms |
-| MSTest | 4.0.2 | 2,964.1 ms | 2,966.5 ms | 9.82 ms |
-| xUnit3 | 3.2.0 | 3,035.9 ms | 3,036.5 ms | 6.47 ms |
-| **TUnit (AOT)** | 1.2.0 | 128.8 ms | 128.8 ms | 0.37 ms |
+| **TUnit** | 1.2.3 | 595.5 ms | 595.7 ms | 2.63 ms |
+| NUnit | 4.4.0 | 1,164.1 ms | 1,163.4 ms | 6.01 ms |
+| MSTest | 4.0.2 | 2,947.1 ms | 2,948.2 ms | 6.02 ms |
+| xUnit3 | 3.2.0 | 3,048.5 ms | 3,046.6 ms | 14.96 ms |
+| **TUnit (AOT)** | 1.2.3 | 130.9 ms | 130.9 ms | 0.41 ms |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "MassiveParallelTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 3644
- bar [567.7, 1165.6, 2964.1, 3035.9, 128.8]
+ y-axis "Time (ms)" 0 --> 3659
+ bar [595.5, 1164.1, 2947.1, 3048.5, 130.9]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.240Z*
+*Last generated: 2025-11-18T00:28:14.219Z*
diff --git a/docs/docs/benchmarks/MatrixTests.md b/docs/docs/benchmarks/MatrixTests.md
index 4fb0d2f8a5..7807cc0d66 100644
--- a/docs/docs/benchmarks/MatrixTests.md
+++ b/docs/docs/benchmarks/MatrixTests.md
@@ -7,7 +7,7 @@ sidebar_position: 5
# MatrixTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 587.68 ms | 588.39 ms | 5.809 ms |
-| NUnit | 4.4.0 | 1,560.52 ms | 1,557.34 ms | 9.306 ms |
-| MSTest | 4.0.2 | 1,522.76 ms | 1,519.51 ms | 9.857 ms |
-| xUnit3 | 3.2.0 | 1,618.75 ms | 1,615.45 ms | 8.652 ms |
-| **TUnit (AOT)** | 1.2.0 | 80.22 ms | 80.11 ms | 0.343 ms |
+| **TUnit** | 1.2.3 | 561.43 ms | 562.61 ms | 4.718 ms |
+| NUnit | 4.4.0 | 1,556.36 ms | 1,557.95 ms | 7.815 ms |
+| MSTest | 4.0.2 | 1,517.35 ms | 1,517.64 ms | 12.824 ms |
+| xUnit3 | 3.2.0 | 1,602.60 ms | 1,603.72 ms | 11.920 ms |
+| **TUnit (AOT)** | 1.2.3 | 79.00 ms | 79.03 ms | 0.223 ms |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "MatrixTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 1943
- bar [587.68, 1560.52, 1522.76, 1618.75, 80.22]
+ y-axis "Time (ms)" 0 --> 1924
+ bar [561.43, 1556.36, 1517.35, 1602.6, 79]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.240Z*
+*Last generated: 2025-11-18T00:28:14.219Z*
diff --git a/docs/docs/benchmarks/ScaleTests.md b/docs/docs/benchmarks/ScaleTests.md
index d582c6831d..368e584fb2 100644
--- a/docs/docs/benchmarks/ScaleTests.md
+++ b/docs/docs/benchmarks/ScaleTests.md
@@ -7,7 +7,7 @@ sidebar_position: 6
# ScaleTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 526.00 ms | 526.16 ms | 5.220 ms |
-| NUnit | 4.4.0 | 619.91 ms | 616.01 ms | 28.759 ms |
-| MSTest | 4.0.2 | 615.62 ms | 614.08 ms | 21.339 ms |
-| xUnit3 | 3.2.0 | 614.08 ms | 614.08 ms | 16.235 ms |
-| **TUnit (AOT)** | 1.2.0 | 46.63 ms | 46.83 ms | 3.501 ms |
+| **TUnit** | 1.2.3 | 541.57 ms | 540.90 ms | 3.848 ms |
+| NUnit | 4.4.0 | 591.49 ms | 591.89 ms | 9.280 ms |
+| MSTest | 4.0.2 | 512.41 ms | 507.96 ms | 11.331 ms |
+| xUnit3 | 3.2.0 | 596.95 ms | 593.96 ms | 9.270 ms |
+| **TUnit (AOT)** | 1.2.3 | 43.84 ms | 43.96 ms | 3.376 ms |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "ScaleTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 744
- bar [526, 619.91, 615.62, 614.08, 46.63]
+ y-axis "Time (ms)" 0 --> 717
+ bar [541.57, 591.49, 512.41, 596.95, 43.84]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.240Z*
+*Last generated: 2025-11-18T00:28:14.219Z*
diff --git a/docs/docs/benchmarks/SetupTeardownTests.md b/docs/docs/benchmarks/SetupTeardownTests.md
index 37623791c6..fa7067085b 100644
--- a/docs/docs/benchmarks/SetupTeardownTests.md
+++ b/docs/docs/benchmarks/SetupTeardownTests.md
@@ -7,7 +7,7 @@ sidebar_position: 7
# SetupTeardownTests Benchmark
:::info Last Updated
-This benchmark was automatically generated on **2025-11-16** from the latest CI run.
+This benchmark was automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -16,11 +16,11 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
| Framework | Version | Mean | Median | StdDev |
|-----------|---------|------|--------|--------|
-| **TUnit** | 1.2.0 | 567.7 ms | 566.9 ms | 7.78 ms |
-| NUnit | 4.4.0 | 1,135.4 ms | 1,134.9 ms | 10.64 ms |
-| MSTest | 4.0.2 | 1,114.5 ms | 1,114.7 ms | 8.36 ms |
-| xUnit3 | 3.2.0 | 1,194.5 ms | 1,193.0 ms | 8.58 ms |
-| **TUnit (AOT)** | 1.2.0 | NA | NA | NA |
+| **TUnit** | 1.2.3 | 578.7 ms | 578.1 ms | 6.18 ms |
+| NUnit | 4.4.0 | 1,182.7 ms | 1,182.8 ms | 8.39 ms |
+| MSTest | 4.0.2 | 1,152.5 ms | 1,151.9 ms | 6.97 ms |
+| xUnit3 | 3.2.0 | 1,229.3 ms | 1,225.5 ms | 9.31 ms |
+| **TUnit (AOT)** | 1.2.3 | NA | NA | NA |
## 📈 Visual Comparison
@@ -58,8 +58,8 @@ This benchmark was automatically generated on **2025-11-16** from the latest CI
xychart-beta
title "SetupTeardownTests Performance Comparison"
x-axis ["TUnit", "NUnit", "MSTest", "xUnit3", "TUnit_AOT"]
- y-axis "Time (ms)" 0 --> 1434
- bar [567.7, 1135.4, 1114.5, 1194.5, 0]
+ y-axis "Time (ms)" 0 --> 1476
+ bar [578.7, 1182.7, 1152.5, 1229.3, 0]
```
## 🎯 Key Insights
@@ -72,4 +72,4 @@ This benchmark compares TUnit's performance against NUnit, MSTest, xUnit3 using
View the [benchmarks overview](/docs/benchmarks) for methodology details and environment information.
:::
-*Last generated: 2025-11-16T00:30:01.241Z*
+*Last generated: 2025-11-18T00:28:14.220Z*
diff --git a/docs/docs/benchmarks/index.md b/docs/docs/benchmarks/index.md
index 931f739ce4..5dcade5491 100644
--- a/docs/docs/benchmarks/index.md
+++ b/docs/docs/benchmarks/index.md
@@ -7,7 +7,7 @@ sidebar_position: 1
# Performance Benchmarks
:::info Last Updated
-These benchmarks were automatically generated on **2025-11-16** from the latest CI run.
+These benchmarks were automatically generated on **2025-11-18** from the latest CI run.
**Environment:** Ubuntu Latest • .NET SDK 10.0.100
:::
@@ -37,7 +37,7 @@ These benchmarks compare TUnit against the most popular .NET testing frameworks:
| Framework | Version Tested |
|-----------|----------------|
-| **TUnit** | 1.2.0 |
+| **TUnit** | 1.2.3 |
| **xUnit v3** | 3.2.0 |
| **NUnit** | 4.4.0 |
| **MSTest** | 4.0.2 |
@@ -80,4 +80,4 @@ These benchmarks run automatically daily via [GitHub Actions](https://github.com
Each benchmark runs multiple iterations with statistical analysis to ensure accuracy. Results may vary based on hardware and test characteristics.
:::
-*Last generated: 2025-11-16T00:30:01.241Z*
+*Last generated: 2025-11-18T00:28:14.220Z*
diff --git a/docs/docs/test-lifecycle/test-context.md b/docs/docs/test-lifecycle/test-context.md
index 6884f43284..5292cce789 100644
--- a/docs/docs/test-lifecycle/test-context.md
+++ b/docs/docs/test-lifecycle/test-context.md
@@ -28,168 +28,11 @@ if (TestContext.Current?.Result?.State == TestState.Failed)
}
```
-## Service Provider Integration
+## Dependency Injection
-`TestContext` provides access to dependency injection services through the `GetService()` and `GetRequiredService()` methods. This allows you to access registered services within your tests, hooks, and custom extensions.
+**Note**: `TestContext` does NOT provide direct access to dependency injection services. The internal service provider in `TestContext` is exclusively for TUnit framework services and is not meant for user-provided dependencies.
-### Accessing Services
-
-```csharp
-[Test]
-public async Task DatabaseTest()
-{
- // Get an optional service (returns null if not registered)
- var logger = TestContext.Current?.GetService>();
- logger?.LogInformation("Starting database test");
-
- // Get a required service (throws if not registered)
- var dbContext = TestContext.Current!.GetRequiredService();
-
- // Use the service
- var users = await dbContext.Users.ToListAsync();
- await Assert.That(users).IsNotEmpty();
-}
-```
-
-### Common Use Cases
-
-#### 1. Accessing Loggers
-
-```csharp
-[Before(HookType.Test)]
-public void LogTestStart()
-{
- var logger = TestContext.Current?.GetService