diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..2789051204
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for DefaultValuesAssertion.
+///
+public static class DefaultValuesAssertionExtensions
+{
+
+ ///
+ /// Extension method for DefaultValuesAssertion.
+ ///
+ public static DefaultValuesAssertion HasDefaultValues(this IAssertionSource source, bool boolValue = true, int intValue = 0, string stringValue = "default", [CallerArgumentExpression(nameof(boolValue))] string? boolValueExpression = null, [CallerArgumentExpression(nameof(intValue))] string? intValueExpression = null, [CallerArgumentExpression(nameof(stringValue))] string? stringValueExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".HasDefaultValues({boolValueExpression}, {intValueExpression}, {stringValueExpression})");
+ return new DefaultValuesAssertion(source.Context, boolValue, intValue, stringValue);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..2789051204
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for DefaultValuesAssertion.
+///
+public static class DefaultValuesAssertionExtensions
+{
+
+ ///
+ /// Extension method for DefaultValuesAssertion.
+ ///
+ public static DefaultValuesAssertion HasDefaultValues(this IAssertionSource source, bool boolValue = true, int intValue = 0, string stringValue = "default", [CallerArgumentExpression(nameof(boolValue))] string? boolValueExpression = null, [CallerArgumentExpression(nameof(intValue))] string? intValueExpression = null, [CallerArgumentExpression(nameof(stringValue))] string? stringValueExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".HasDefaultValues({boolValueExpression}, {intValueExpression}, {stringValueExpression})");
+ return new DefaultValuesAssertion(source.Context, boolValue, intValue, stringValue);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..2789051204
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for DefaultValuesAssertion.
+///
+public static class DefaultValuesAssertionExtensions
+{
+
+ ///
+ /// Extension method for DefaultValuesAssertion.
+ ///
+ public static DefaultValuesAssertion HasDefaultValues(this IAssertionSource source, bool boolValue = true, int intValue = 0, string stringValue = "default", [CallerArgumentExpression(nameof(boolValue))] string? boolValueExpression = null, [CallerArgumentExpression(nameof(intValue))] string? intValueExpression = null, [CallerArgumentExpression(nameof(stringValue))] string? stringValueExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".HasDefaultValues({boolValueExpression}, {intValueExpression}, {stringValueExpression})");
+ return new DefaultValuesAssertion(source.Context, boolValue, intValue, stringValue);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.Net4_7.verified.txt
new file mode 100644
index 0000000000..2789051204
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithDefaultValues.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for DefaultValuesAssertion.
+///
+public static class DefaultValuesAssertionExtensions
+{
+
+ ///
+ /// Extension method for DefaultValuesAssertion.
+ ///
+ public static DefaultValuesAssertion HasDefaultValues(this IAssertionSource source, bool boolValue = true, int intValue = 0, string stringValue = "default", [CallerArgumentExpression(nameof(boolValue))] string? boolValueExpression = null, [CallerArgumentExpression(nameof(intValue))] string? intValueExpression = null, [CallerArgumentExpression(nameof(stringValue))] string? stringValueExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".HasDefaultValues({boolValueExpression}, {intValueExpression}, {stringValueExpression})");
+ return new DefaultValuesAssertion(source.Context, boolValue, intValue, stringValue);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..7feb94b908
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringComparisonAssertion.
+///
+public static class StringComparisonAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringComparisonAssertion.
+ ///
+ public static StringComparisonAssertion IsEqualToWithComparison(this IAssertionSource source, string expected, System.StringComparison comparison = System.StringComparison.4, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparison))] string? comparisonExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualToWithComparison({expectedExpression}, {comparisonExpression})");
+ return new StringComparisonAssertion(source.Context, expected, comparison);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..7feb94b908
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringComparisonAssertion.
+///
+public static class StringComparisonAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringComparisonAssertion.
+ ///
+ public static StringComparisonAssertion IsEqualToWithComparison(this IAssertionSource source, string expected, System.StringComparison comparison = System.StringComparison.4, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparison))] string? comparisonExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualToWithComparison({expectedExpression}, {comparisonExpression})");
+ return new StringComparisonAssertion(source.Context, expected, comparison);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..7feb94b908
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringComparisonAssertion.
+///
+public static class StringComparisonAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringComparisonAssertion.
+ ///
+ public static StringComparisonAssertion IsEqualToWithComparison(this IAssertionSource source, string expected, System.StringComparison comparison = System.StringComparison.4, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparison))] string? comparisonExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualToWithComparison({expectedExpression}, {comparisonExpression})");
+ return new StringComparisonAssertion(source.Context, expected, comparison);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.Net4_7.verified.txt
new file mode 100644
index 0000000000..7feb94b908
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithEnumDefault.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringComparisonAssertion.
+///
+public static class StringComparisonAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringComparisonAssertion.
+ ///
+ public static StringComparisonAssertion IsEqualToWithComparison(this IAssertionSource source, string expected, System.StringComparison comparison = System.StringComparison.4, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparison))] string? comparisonExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualToWithComparison({expectedExpression}, {comparisonExpression})");
+ return new StringComparisonAssertion(source.Context, expected, comparison);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..96d04a72e4
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet10_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for GreaterThanAssertion.
+///
+public static class GreaterThanAssertionExtensions
+{
+
+ ///
+ /// Extension method for GreaterThanAssertion.
+ ///
+ public static GreaterThanAssertion IsGreaterThan(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsGreaterThan({expectedExpression})");
+ return new GreaterThanAssertion(source.Context, expected);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..96d04a72e4
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet8_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for GreaterThanAssertion.
+///
+public static class GreaterThanAssertionExtensions
+{
+
+ ///
+ /// Extension method for GreaterThanAssertion.
+ ///
+ public static GreaterThanAssertion IsGreaterThan(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsGreaterThan({expectedExpression})");
+ return new GreaterThanAssertion(source.Context, expected);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..96d04a72e4
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.DotNet9_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for GreaterThanAssertion.
+///
+public static class GreaterThanAssertionExtensions
+{
+
+ ///
+ /// Extension method for GreaterThanAssertion.
+ ///
+ public static GreaterThanAssertion IsGreaterThan(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsGreaterThan({expectedExpression})");
+ return new GreaterThanAssertion(source.Context, expected);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.Net4_7.verified.txt
new file mode 100644
index 0000000000..96d04a72e4
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithGenericConstraints.Net4_7.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for GreaterThanAssertion.
+///
+public static class GreaterThanAssertionExtensions
+{
+
+ ///
+ /// Extension method for GreaterThanAssertion.
+ ///
+ public static GreaterThanAssertion IsGreaterThan(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsGreaterThan({expectedExpression})");
+ return new GreaterThanAssertion(source.Context, expected);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..f43d45e395
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet10_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for EqualsAssertion.
+///
+public static class EqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression})");
+ return new EqualsAssertion(source.Context, expected);
+ }
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, System.Collections.Generic.IEqualityComparer comparer, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression}, {comparerExpression})");
+ return new EqualsAssertion(source.Context, expected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..f43d45e395
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet8_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for EqualsAssertion.
+///
+public static class EqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression})");
+ return new EqualsAssertion(source.Context, expected);
+ }
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, System.Collections.Generic.IEqualityComparer comparer, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression}, {comparerExpression})");
+ return new EqualsAssertion(source.Context, expected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..f43d45e395
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.DotNet9_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for EqualsAssertion.
+///
+public static class EqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression})");
+ return new EqualsAssertion(source.Context, expected);
+ }
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, System.Collections.Generic.IEqualityComparer comparer, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression}, {comparerExpression})");
+ return new EqualsAssertion(source.Context, expected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.Net4_7.verified.txt
new file mode 100644
index 0000000000..f43d45e395
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleConstructors.Net4_7.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for EqualsAssertion.
+///
+public static class EqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression})");
+ return new EqualsAssertion(source.Context, expected);
+ }
+
+ ///
+ /// Extension method for EqualsAssertion.
+ ///
+ public static EqualsAssertion IsEqualTo(this IAssertionSource source, TValue expected, System.Collections.Generic.IEqualityComparer comparer, [CallerArgumentExpression(nameof(expected))] string? expectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEqualTo({expectedExpression}, {comparerExpression})");
+ return new EqualsAssertion(source.Context, expected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..fd7d9c4ade
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet10_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for BetweenAssertion.
+///
+public static class BetweenAssertionExtensions
+{
+
+ ///
+ /// Extension method for BetweenAssertion.
+ ///
+ public static BetweenAssertion IsBetween(this IAssertionSource source, TValue lowerBound, TValue upperBound, [CallerArgumentExpression(nameof(lowerBound))] string? lowerBoundExpression = null, [CallerArgumentExpression(nameof(upperBound))] string? upperBoundExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsBetween({lowerBoundExpression}, {upperBoundExpression})");
+ return new BetweenAssertion(source.Context, lowerBound, upperBound);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..fd7d9c4ade
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet8_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for BetweenAssertion.
+///
+public static class BetweenAssertionExtensions
+{
+
+ ///
+ /// Extension method for BetweenAssertion.
+ ///
+ public static BetweenAssertion IsBetween(this IAssertionSource source, TValue lowerBound, TValue upperBound, [CallerArgumentExpression(nameof(lowerBound))] string? lowerBoundExpression = null, [CallerArgumentExpression(nameof(upperBound))] string? upperBoundExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsBetween({lowerBoundExpression}, {upperBoundExpression})");
+ return new BetweenAssertion(source.Context, lowerBound, upperBound);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..fd7d9c4ade
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.DotNet9_0.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for BetweenAssertion.
+///
+public static class BetweenAssertionExtensions
+{
+
+ ///
+ /// Extension method for BetweenAssertion.
+ ///
+ public static BetweenAssertion IsBetween(this IAssertionSource source, TValue lowerBound, TValue upperBound, [CallerArgumentExpression(nameof(lowerBound))] string? lowerBoundExpression = null, [CallerArgumentExpression(nameof(upperBound))] string? upperBoundExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsBetween({lowerBoundExpression}, {upperBoundExpression})");
+ return new BetweenAssertion(source.Context, lowerBound, upperBound);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.Net4_7.verified.txt
new file mode 100644
index 0000000000..fd7d9c4ade
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithMultipleParameters.Net4_7.verified.txt
@@ -0,0 +1,28 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for BetweenAssertion.
+///
+public static class BetweenAssertionExtensions
+{
+
+ ///
+ /// Extension method for BetweenAssertion.
+ ///
+ public static BetweenAssertion IsBetween(this IAssertionSource source, TValue lowerBound, TValue upperBound, [CallerArgumentExpression(nameof(lowerBound))] string? lowerBoundExpression = null, [CallerArgumentExpression(nameof(upperBound))] string? upperBoundExpression = null)
+ where TValue : System.IComparable
+ {
+ source.Context.ExpressionBuilder.Append($".IsBetween({lowerBoundExpression}, {upperBoundExpression})");
+ return new BetweenAssertion(source.Context, lowerBound, upperBound);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..74801947d8
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet10_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for TrueAssertion.
+///
+public static class TrueAssertionExtensions
+{
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsTrue(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsTrue()");
+ return new TrueAssertion(source.Context);
+ }
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsFalse(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsFalse()");
+ return new TrueAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..74801947d8
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet8_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for TrueAssertion.
+///
+public static class TrueAssertionExtensions
+{
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsTrue(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsTrue()");
+ return new TrueAssertion(source.Context);
+ }
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsFalse(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsFalse()");
+ return new TrueAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..74801947d8
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.DotNet9_0.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for TrueAssertion.
+///
+public static class TrueAssertionExtensions
+{
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsTrue(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsTrue()");
+ return new TrueAssertion(source.Context);
+ }
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsFalse(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsFalse()");
+ return new TrueAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.Net4_7.verified.txt
new file mode 100644
index 0000000000..74801947d8
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithNegatedMethod.Net4_7.verified.txt
@@ -0,0 +1,36 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for TrueAssertion.
+///
+public static class TrueAssertionExtensions
+{
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsTrue(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsTrue()");
+ return new TrueAssertion(source.Context);
+ }
+
+ ///
+ /// Extension method for TrueAssertion.
+ ///
+ public static TrueAssertion IsFalse(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsFalse()");
+ return new TrueAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..38721800fe
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NotEqualsAssertion.
+///
+public static class NotEqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for NotEqualsAssertion.
+ ///
+ public static NotEqualsAssertion IsNotEqualTo(this IAssertionSource source, TValue notExpected, System.Collections.Generic.IEqualityComparer? comparer = null, [CallerArgumentExpression(nameof(notExpected))] string? notExpectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNotEqualTo({notExpectedExpression}, {comparerExpression})");
+ return new NotEqualsAssertion(source.Context, notExpected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..38721800fe
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NotEqualsAssertion.
+///
+public static class NotEqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for NotEqualsAssertion.
+ ///
+ public static NotEqualsAssertion IsNotEqualTo(this IAssertionSource source, TValue notExpected, System.Collections.Generic.IEqualityComparer? comparer = null, [CallerArgumentExpression(nameof(notExpected))] string? notExpectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNotEqualTo({notExpectedExpression}, {comparerExpression})");
+ return new NotEqualsAssertion(source.Context, notExpected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..38721800fe
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NotEqualsAssertion.
+///
+public static class NotEqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for NotEqualsAssertion.
+ ///
+ public static NotEqualsAssertion IsNotEqualTo(this IAssertionSource source, TValue notExpected, System.Collections.Generic.IEqualityComparer? comparer = null, [CallerArgumentExpression(nameof(notExpected))] string? notExpectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNotEqualTo({notExpectedExpression}, {comparerExpression})");
+ return new NotEqualsAssertion(source.Context, notExpected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.Net4_7.verified.txt
new file mode 100644
index 0000000000..38721800fe
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.AssertionWithOptionalParameter.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NotEqualsAssertion.
+///
+public static class NotEqualsAssertionExtensions
+{
+
+ ///
+ /// Extension method for NotEqualsAssertion.
+ ///
+ public static NotEqualsAssertion IsNotEqualTo(this IAssertionSource source, TValue notExpected, System.Collections.Generic.IEqualityComparer? comparer = null, [CallerArgumentExpression(nameof(notExpected))] string? notExpectedExpression = null, [CallerArgumentExpression(nameof(comparer))] string? comparerExpression = null)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNotEqualTo({notExpectedExpression}, {comparerExpression})");
+ return new NotEqualsAssertion(source.Context, notExpected, comparer);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..0e082051dd
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for IsAssignableToAssertion.
+///
+public static class IsAssignableToAssertionExtensions
+{
+
+ ///
+ /// Extension method for IsAssignableToAssertion.
+ ///
+ public static IsAssignableToAssertion IsAssignableTo(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsAssignableTo()");
+ return new IsAssignableToAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..0e082051dd
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for IsAssignableToAssertion.
+///
+public static class IsAssignableToAssertionExtensions
+{
+
+ ///
+ /// Extension method for IsAssignableToAssertion.
+ ///
+ public static IsAssignableToAssertion IsAssignableTo(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsAssignableTo()");
+ return new IsAssignableToAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..0e082051dd
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for IsAssignableToAssertion.
+///
+public static class IsAssignableToAssertionExtensions
+{
+
+ ///
+ /// Extension method for IsAssignableToAssertion.
+ ///
+ public static IsAssignableToAssertion IsAssignableTo(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsAssignableTo()");
+ return new IsAssignableToAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.Net4_7.verified.txt
new file mode 100644
index 0000000000..0e082051dd
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.MultipleGenericParameters.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for IsAssignableToAssertion.
+///
+public static class IsAssignableToAssertionExtensions
+{
+
+ ///
+ /// Extension method for IsAssignableToAssertion.
+ ///
+ public static IsAssignableToAssertion IsAssignableTo(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsAssignableTo()");
+ return new IsAssignableToAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..5a77024fe7
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringIsEmptyAssertion.
+///
+public static class StringIsEmptyAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringIsEmptyAssertion.
+ ///
+ public static StringIsEmptyAssertion IsEmpty(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEmpty()");
+ return new StringIsEmptyAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..5a77024fe7
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringIsEmptyAssertion.
+///
+public static class StringIsEmptyAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringIsEmptyAssertion.
+ ///
+ public static StringIsEmptyAssertion IsEmpty(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEmpty()");
+ return new StringIsEmptyAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..5a77024fe7
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringIsEmptyAssertion.
+///
+public static class StringIsEmptyAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringIsEmptyAssertion.
+ ///
+ public static StringIsEmptyAssertion IsEmpty(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEmpty()");
+ return new StringIsEmptyAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.Net4_7.verified.txt
new file mode 100644
index 0000000000..5a77024fe7
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.NonGenericAssertion.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for StringIsEmptyAssertion.
+///
+public static class StringIsEmptyAssertionExtensions
+{
+
+ ///
+ /// Extension method for StringIsEmptyAssertion.
+ ///
+ public static StringIsEmptyAssertion IsEmpty(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsEmpty()");
+ return new StringIsEmptyAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet10_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet10_0.verified.txt
new file mode 100644
index 0000000000..110bb7c5c0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet10_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NullAssertion.
+///
+public static class NullAssertionExtensions
+{
+
+ ///
+ /// Extension method for NullAssertion.
+ ///
+ public static NullAssertion IsNull(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNull()");
+ return new NullAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet8_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet8_0.verified.txt
new file mode 100644
index 0000000000..110bb7c5c0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet8_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NullAssertion.
+///
+public static class NullAssertionExtensions
+{
+
+ ///
+ /// Extension method for NullAssertion.
+ ///
+ public static NullAssertion IsNull(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNull()");
+ return new NullAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet9_0.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet9_0.verified.txt
new file mode 100644
index 0000000000..110bb7c5c0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.DotNet9_0.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NullAssertion.
+///
+public static class NullAssertionExtensions
+{
+
+ ///
+ /// Extension method for NullAssertion.
+ ///
+ public static NullAssertion IsNull(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNull()");
+ return new NullAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.Net4_7.verified.txt b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.Net4_7.verified.txt
new file mode 100644
index 0000000000..110bb7c5c0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.SingleGenericParameter.Net4_7.verified.txt
@@ -0,0 +1,27 @@
+[
+#nullable enable
+
+using System;
+using System.Runtime.CompilerServices;
+using TUnit.Assertions.Core;
+using TUnit.Assertions.Tests.TestData;
+
+namespace TUnit.Assertions.Extensions;
+
+///
+/// Generated extension methods for NullAssertion.
+///
+public static class NullAssertionExtensions
+{
+
+ ///
+ /// Extension method for NullAssertion.
+ ///
+ public static NullAssertion IsNull(this IAssertionSource source)
+ {
+ source.Context.ExpressionBuilder.Append($".IsNull()");
+ return new NullAssertion(source.Context);
+ }
+}
+
+]
\ No newline at end of file
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs
new file mode 100644
index 0000000000..dafa3112b1
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/AssertionExtensionGeneratorTests.cs
@@ -0,0 +1,139 @@
+using TUnit.Assertions.SourceGenerator.Generators;
+using TUnit.Assertions.SourceGenerator.Tests.Options;
+
+namespace TUnit.Assertions.SourceGenerator.Tests;
+
+internal class AssertionExtensionGeneratorTests : TestsBase
+{
+ [Test]
+ public Task NonGenericAssertion() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "NonGenericAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsEmpty");
+ });
+
+ [Test]
+ public Task SingleGenericParameter() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "SingleGenericParameterAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsNull");
+ await Assert.That(generatedFiles[0]).Contains("");
+ });
+
+ [Test]
+ public Task MultipleGenericParameters() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "MultipleGenericParametersAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsAssignableTo");
+ await Assert.That(generatedFiles[0]).Contains("");
+ });
+
+ [Test]
+ public Task AssertionWithOptionalParameter() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "OptionalParameterAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsNotEqualTo");
+ await Assert.That(generatedFiles[0]).Contains("= null");
+ });
+
+ [Test]
+ public Task AssertionWithGenericConstraints() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "GenericConstraintsAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsGreaterThan");
+ await Assert.That(generatedFiles[0]).Contains("where TValue : System.IComparable");
+ });
+
+ [Test]
+ public Task AssertionWithMultipleConstructors() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "MultipleConstructorsAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ // Should generate multiple overloads
+ var containsCount = System.Text.RegularExpressions.Regex.Matches(generatedFiles[0], "public static.*IsEqualTo").Count;
+ await Assert.That(containsCount).IsGreaterThan(1);
+ });
+
+ [Test]
+ public Task AssertionWithNegatedMethod() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "NegatedMethodAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsTrue");
+ await Assert.That(generatedFiles[0]).Contains("IsFalse");
+ });
+
+ [Test]
+ public Task AssertionWithDefaultValues() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "DefaultValuesAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("= true");
+ await Assert.That(generatedFiles[0]).Contains("= 0");
+ await Assert.That(generatedFiles[0]).Contains("= \"default\"");
+ });
+
+ [Test]
+ public Task AssertionWithEnumDefault() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "EnumDefaultAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("StringComparison.");
+ });
+
+ [Test]
+ public Task AssertionWithMultipleParameters() => RunTest(
+ Path.Combine(Sourcy.Git.RootDirectory.FullName,
+ "TUnit.Assertions.SourceGenerator.Tests",
+ "TestData",
+ "MultipleParametersAssertion.cs"),
+ async generatedFiles =>
+ {
+ await Assert.That(generatedFiles).HasCount().GreaterThanOrEqualTo(1);
+ await Assert.That(generatedFiles[0]).Contains("IsBetween");
+ // Should have CallerArgumentExpression for both parameters
+ var callerExprCount = System.Text.RegularExpressions.Regex.Matches(generatedFiles[0], "CallerArgumentExpression").Count;
+ await Assert.That(callerExprCount).IsGreaterThanOrEqualTo(2);
+ });
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorTests.cs b/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorTests.cs
deleted file mode 100644
index 1dedcdde02..0000000000
--- a/TUnit.Assertions.SourceGenerator.Tests/AssertionMethodGeneratorTests.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using TUnit.Assertions.SourceGenerator.Generators;
-
-namespace TUnit.Assertions.SourceGenerator.Tests;
-
-internal class AssertionMethodGeneratorTests : TestsBase
-{
- [Test]
- public Task GeneratesCharAssertions() => RunTest(Path.Combine(Sourcy.Git.RootDirectory.FullName,
- "TUnit.Assertions",
- "Assertions",
- "CharAssertionExtensions.cs"),
- _ => Task.CompletedTask);
-
- [Test]
- public Task GeneratesEnumAssertions() => RunTest(Path.Combine(Sourcy.Git.RootDirectory.FullName,
- "TUnit.Assertions",
- "Assertions",
- "EnumAssertionExtensions.cs"),
- _ => Task.CompletedTask);
-
- [Test]
- public Task GeneratesPathAssertions() => RunTest(Path.Combine(Sourcy.Git.RootDirectory.FullName,
- "TUnit.Assertions",
- "Assertions",
- "PathAssertionExtensions.cs"),
- _ => Task.CompletedTask);
-
- [Test]
- public Task GeneratesStringAssertions() => RunTest(Path.Combine(Sourcy.Git.RootDirectory.FullName,
- "TUnit.Assertions",
- "Assertions",
- "StringAssertionExtensions.cs"),
- _ => Task.CompletedTask);
-
- [Test]
- public Task GeneratesUriAssertions() => RunTest(Path.Combine(Sourcy.Git.RootDirectory.FullName,
- "TUnit.Assertions",
- "Assertions",
- "UriAssertionExtensions.cs"),
- _ => Task.CompletedTask);
-}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/DefaultValuesAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/DefaultValuesAssertion.cs
new file mode 100644
index 0000000000..9c94d5f792
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/DefaultValuesAssertion.cs
@@ -0,0 +1,35 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with various default parameter values
+/// Should generate extension method with properly formatted defaults
+///
+[AssertionExtension("HasDefaultValues")]
+public class DefaultValuesAssertion : Assertion
+{
+ private readonly bool _boolValue;
+ private readonly int _intValue;
+ private readonly string _stringValue;
+
+ public DefaultValuesAssertion(
+ AssertionContext context,
+ bool boolValue = true,
+ int intValue = 0,
+ string stringValue = "default")
+ : base(context)
+ {
+ _boolValue = boolValue;
+ _intValue = intValue;
+ _stringValue = stringValue;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ protected override string GetExpectation() => "to have default values";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/EnumDefaultAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/EnumDefaultAssertion.cs
new file mode 100644
index 0000000000..75103aa034
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/EnumDefaultAssertion.cs
@@ -0,0 +1,39 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with enum default parameter
+/// Should generate extension method with properly formatted enum default
+///
+[AssertionExtension("IsEqualToWithComparison")]
+public class StringComparisonAssertion : Assertion
+{
+ private readonly string _expected;
+ private readonly StringComparison _comparison;
+
+ public StringComparisonAssertion(
+ AssertionContext context,
+ string expected,
+ StringComparison comparison = StringComparison.Ordinal)
+ : base(context)
+ {
+ _expected = expected;
+ _comparison = comparison;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (string.Equals(value, _expected, _comparison))
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"'{value}' does not equal '{_expected}' with {_comparison}"));
+ }
+
+ protected override string GetExpectation() => $"to equal '{_expected}' with {_comparison}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/GenericConstraintsAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/GenericConstraintsAssertion.cs
new file mode 100644
index 0000000000..47282ef311
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/GenericConstraintsAssertion.cs
@@ -0,0 +1,42 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with generic constraint
+/// Should generate extension method preserving the IComparable constraint
+///
+[AssertionExtension("IsGreaterThan")]
+public class GreaterThanAssertion : Assertion
+ where TValue : IComparable
+{
+ private readonly TValue _expected;
+
+ public GreaterThanAssertion(
+ AssertionContext context,
+ TValue expected)
+ : base(context)
+ {
+ _expected = expected;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (value == null)
+ {
+ return Task.FromResult(AssertionResult.Failed("value was null"));
+ }
+
+ if (value.CompareTo(_expected) > 0)
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"value {value} was not greater than {_expected}"));
+ }
+
+ protected override string GetExpectation() => $"to be greater than {_expected}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleConstructorsAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleConstructorsAssertion.cs
new file mode 100644
index 0000000000..67138cd2c0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleConstructorsAssertion.cs
@@ -0,0 +1,51 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with multiple constructors
+/// Should generate multiple extension method overloads
+///
+[AssertionExtension("IsEqualTo")]
+public class EqualsAssertion : Assertion
+{
+ private readonly TValue _expected;
+ private readonly IEqualityComparer? _comparer;
+
+ // Constructor 1: Just expected value
+ public EqualsAssertion(
+ AssertionContext context,
+ TValue expected)
+ : base(context)
+ {
+ _expected = expected;
+ _comparer = null;
+ }
+
+ // Constructor 2: Expected value with comparer
+ public EqualsAssertion(
+ AssertionContext context,
+ TValue expected,
+ IEqualityComparer comparer)
+ : base(context)
+ {
+ _expected = expected;
+ _comparer = comparer;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+ var comparer = _comparer ?? EqualityComparer.Default;
+
+ if (comparer.Equals(value!, _expected))
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"found {value}"));
+ }
+
+ protected override string GetExpectation() => $"to be equal to {_expected}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleGenericParametersAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleGenericParametersAssertion.cs
new file mode 100644
index 0000000000..97e98510de
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleGenericParametersAssertion.cs
@@ -0,0 +1,41 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Multiple generic parameters assertion
+/// Should generate extension method with two generic type parameters
+///
+[AssertionExtension("IsAssignableTo")]
+public class IsAssignableToAssertion : Assertion
+{
+ private readonly Type _targetType;
+
+ public IsAssignableToAssertion(AssertionContext context)
+ : base(context)
+ {
+ _targetType = typeof(TTarget);
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (value == null)
+ {
+ return Task.FromResult(AssertionResult.Failed("value was null"));
+ }
+
+ var actualType = value.GetType();
+
+ if (_targetType.IsAssignableFrom(actualType))
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"type {actualType.Name} is not assignable to {_targetType.Name}"));
+ }
+
+ protected override string GetExpectation() => $"to be assignable to {_targetType.Name}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleParametersAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleParametersAssertion.cs
new file mode 100644
index 0000000000..09c22d3cd8
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/MultipleParametersAssertion.cs
@@ -0,0 +1,45 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with multiple parameters
+/// Should generate extension method with CallerArgumentExpression for all parameters
+///
+[AssertionExtension("IsBetween")]
+public class BetweenAssertion : Assertion
+ where TValue : IComparable
+{
+ private readonly TValue _lowerBound;
+ private readonly TValue _upperBound;
+
+ public BetweenAssertion(
+ AssertionContext context,
+ TValue lowerBound,
+ TValue upperBound)
+ : base(context)
+ {
+ _lowerBound = lowerBound;
+ _upperBound = upperBound;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (value == null)
+ {
+ return Task.FromResult(AssertionResult.Failed("value was null"));
+ }
+
+ if (value.CompareTo(_lowerBound) >= 0 && value.CompareTo(_upperBound) <= 0)
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"value {value} was not between {_lowerBound} and {_upperBound}"));
+ }
+
+ protected override string GetExpectation() => $"to be between {_lowerBound} and {_upperBound}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/NegatedMethodAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/NegatedMethodAssertion.cs
new file mode 100644
index 0000000000..058aae2c07
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/NegatedMethodAssertion.cs
@@ -0,0 +1,31 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with negated method name
+/// Should generate both positive and negative extension methods
+///
+[AssertionExtension("IsTrue", NegatedMethodName = "IsFalse")]
+public class TrueAssertion : Assertion
+{
+ public TrueAssertion(AssertionContext context)
+ : base(context)
+ {
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (value)
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed("value was false"));
+ }
+
+ protected override string GetExpectation() => "to be true";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/NonGenericAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/NonGenericAssertion.cs
new file mode 100644
index 0000000000..ff6b06c3a9
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/NonGenericAssertion.cs
@@ -0,0 +1,31 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Non-generic assertion class
+/// Should generate extension method without generic parameters
+///
+[AssertionExtension("IsEmpty")]
+public class StringIsEmptyAssertion : Assertion
+{
+ public StringIsEmptyAssertion(AssertionContext context)
+ : base(context)
+ {
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (string.IsNullOrEmpty(value))
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"String was not empty: '{value}'"));
+ }
+
+ protected override string GetExpectation() => "to be empty";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/OptionalParameterAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/OptionalParameterAssertion.cs
new file mode 100644
index 0000000000..9d5b6163e0
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/OptionalParameterAssertion.cs
@@ -0,0 +1,40 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Assertion with optional parameter
+/// Should generate extension method with default parameter value
+///
+[AssertionExtension("IsNotEqualTo")]
+public class NotEqualsAssertion : Assertion
+{
+ private readonly TValue _notExpected;
+ private readonly IEqualityComparer? _comparer;
+
+ public NotEqualsAssertion(
+ AssertionContext context,
+ TValue notExpected,
+ IEqualityComparer? comparer = null)
+ : base(context)
+ {
+ _notExpected = notExpected;
+ _comparer = comparer;
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+ var comparer = _comparer ?? EqualityComparer.Default;
+
+ if (!comparer.Equals(value!, _notExpected))
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"both values are {value}"));
+ }
+
+ protected override string GetExpectation() => $"to not be equal to {_notExpected}";
+}
diff --git a/TUnit.Assertions.SourceGenerator.Tests/TestData/SingleGenericParameterAssertion.cs b/TUnit.Assertions.SourceGenerator.Tests/TestData/SingleGenericParameterAssertion.cs
new file mode 100644
index 0000000000..f1cab49718
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator.Tests/TestData/SingleGenericParameterAssertion.cs
@@ -0,0 +1,31 @@
+using TUnit.Assertions.Attributes;
+using TUnit.Assertions.Core;
+
+namespace TUnit.Assertions.Tests.TestData;
+
+///
+/// Test case: Single generic parameter assertion
+/// Should generate extension method with one generic type parameter
+///
+[AssertionExtension("IsNull")]
+public class NullAssertion : Assertion
+{
+ public NullAssertion(AssertionContext context)
+ : base(context)
+ {
+ }
+
+ protected override Task CheckAsync(EvaluationMetadata metadata)
+ {
+ var value = metadata.Value;
+
+ if (value == null)
+ {
+ return Task.FromResult(AssertionResult.Passed);
+ }
+
+ return Task.FromResult(AssertionResult.Failed($"Value was not null: {value}"));
+ }
+
+ protected override string GetExpectation() => "to be null";
+}
diff --git a/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
new file mode 100644
index 0000000000..d1f04a2878
--- /dev/null
+++ b/TUnit.Assertions.SourceGenerator/Generators/AssertionExtensionGenerator.cs
@@ -0,0 +1,414 @@
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace TUnit.Assertions.SourceGenerator.Generators;
+
+///
+/// Source generator that creates extension methods for assertion classes decorated with [AssertionExtension].
+/// Analyzes the constructors of assertion classes and generates corresponding extension methods that:
+/// - Extend IAssertionSource<T>
+/// - Build the expression string for error messages
+/// - Instantiate the assertion with the provided parameters
+///
+[Generator]
+public sealed class AssertionExtensionGenerator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // DIAGNOSTIC: Always generate a test file to verify generator is running
+ context.RegisterPostInitializationOutput(ctx =>
+ {
+ ctx.AddSource("_DiagnosticTest.g.cs", @"
+// This file is generated to verify the AssertionExtensionGenerator is running.
+// If you see this file, the generator executed successfully.
+namespace TUnit.Assertions.Diagnostics
+{
+ internal static class GeneratorDiagnostic
+ {
+ public const string Message = ""AssertionExtensionGenerator is running"";
+ }
+}
+");
+ });
+
+ // Find all classes decorated with [AssertionExtension]
+ var assertionClasses = context.SyntaxProvider
+ .ForAttributeWithMetadataName(
+ "TUnit.Assertions.Attributes.AssertionExtensionAttribute",
+ predicate: static (node, _) => node is ClassDeclarationSyntax,
+ transform: static (ctx, ct) => GetAssertionExtensionData(ctx, ct))
+ .Where(static x => x != null)
+ .Select(static (x, _) => x!);
+
+ // Generate extension methods for each assertion class
+ context.RegisterSourceOutput(assertionClasses, static (context, data) => GenerateExtensionMethods(context, data));
+ }
+
+ private static AssertionExtensionData? GetAssertionExtensionData(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken)
+ {
+ if (context.TargetSymbol is not INamedTypeSymbol classSymbol)
+ {
+ return null;
+ }
+
+ // Get the [AssertionExtension] attribute
+ var attributeData = context.Attributes.FirstOrDefault();
+ if (attributeData == null)
+ {
+ return null;
+ }
+
+ // Extract method name from attribute
+ string? methodName = null;
+ if (attributeData.ConstructorArguments.Length > 0)
+ {
+ methodName = attributeData.ConstructorArguments[0].Value?.ToString();
+ }
+
+ if (string.IsNullOrEmpty(methodName))
+ {
+ return null;
+ }
+
+ // Extract optional negated method name
+ string? negatedMethodName = null;
+ foreach (var namedArg in attributeData.NamedArguments)
+ {
+ if (namedArg.Key == "NegatedMethodName")
+ {
+ negatedMethodName = namedArg.Value.Value?.ToString();
+ }
+ }
+
+ // Find the base Assertion type to extract the type parameter
+ var assertionBaseType = GetAssertionBaseType(classSymbol);
+ if (assertionBaseType == null)
+ {
+ // Not an Assertion - skip
+ return null;
+ }
+
+ // Get all public constructors
+ var constructors = classSymbol.Constructors
+ .Where(c => c.DeclaredAccessibility == Accessibility.Public && !c.IsStatic)
+ .ToImmutableArray();
+
+ if (constructors.Length == 0)
+ {
+ return null;
+ }
+
+ return new AssertionExtensionData(
+ classSymbol,
+ methodName!,
+ negatedMethodName,
+ assertionBaseType,
+ constructors
+ );
+ }
+
+ private static INamedTypeSymbol? GetAssertionBaseType(INamedTypeSymbol classSymbol)
+ {
+ var currentType = classSymbol.BaseType;
+ while (currentType != null)
+ {
+ if (currentType.Name == "Assertion" &&
+ currentType.ContainingNamespace?.ToDisplayString() == "TUnit.Assertions.Core" &&
+ currentType.TypeArguments.Length == 1)
+ {
+ return currentType;
+ }
+ currentType = currentType.BaseType;
+ }
+ return null;
+ }
+
+ private static void GenerateExtensionMethods(SourceProductionContext context, AssertionExtensionData data)
+ {
+ var sourceBuilder = new StringBuilder();
+
+ // Header
+ sourceBuilder.AppendLine("#nullable enable");
+ sourceBuilder.AppendLine();
+ sourceBuilder.AppendLine("using System;");
+ sourceBuilder.AppendLine("using System.Runtime.CompilerServices;");
+ sourceBuilder.AppendLine("using TUnit.Assertions.Core;");
+
+ // Add using for the assertion class's namespace if different
+ var assertionNamespace = data.ClassSymbol.ContainingNamespace?.ToDisplayString();
+ if (!string.IsNullOrEmpty(assertionNamespace) && assertionNamespace != "TUnit.Assertions.Extensions")
+ {
+ sourceBuilder.AppendLine($"using {assertionNamespace};");
+ }
+
+ sourceBuilder.AppendLine();
+
+ // Namespace
+
+ sourceBuilder.AppendLine($"namespace TUnit.Assertions.Extensions;");
+ sourceBuilder.AppendLine();
+
+ // Extension class
+ var extensionClassName = $"{data.ClassSymbol.Name}Extensions";
+ sourceBuilder.AppendLine($"/// ");
+ sourceBuilder.AppendLine($"/// Generated extension methods for {data.ClassSymbol.Name}.");
+ sourceBuilder.AppendLine($"/// ");
+ sourceBuilder.AppendLine($"public static class {extensionClassName}");
+ sourceBuilder.AppendLine("{");
+
+ // Generate extension methods for each constructor
+ foreach (var constructor in data.Constructors)
+ {
+ if (!IsValidConstructor(constructor))
+ {
+ continue;
+ }
+
+ // Generate positive assertion method
+ GenerateExtensionMethod(sourceBuilder, data, constructor, negated: false);
+
+ // Generate negated assertion method if requested
+ if (!string.IsNullOrEmpty(data.NegatedMethodName))
+ {
+ GenerateExtensionMethod(sourceBuilder, data, constructor, negated: true);
+ }
+ }
+
+ sourceBuilder.AppendLine("}");
+
+ // Add source to compilation
+ var fileName = $"{data.ClassSymbol.Name}.Extensions.g.cs";
+ context.AddSource(fileName, sourceBuilder.ToString());
+ }
+
+ private static bool IsValidConstructor(IMethodSymbol constructor)
+ {
+ // Must have at least one parameter
+ if (constructor.Parameters.Length == 0)
+ {
+ return false;
+ }
+
+ // First parameter must be AssertionContext
+ var firstParam = constructor.Parameters[0];
+ if (firstParam.Type is not INamedTypeSymbol firstParamType)
+ {
+ return false;
+ }
+
+ return firstParamType.Name == "AssertionContext" &&
+ firstParamType.ContainingNamespace?.ToDisplayString() == "TUnit.Assertions.Core" &&
+ firstParamType.TypeArguments.Length == 1;
+ }
+
+ private static void GenerateExtensionMethod(
+ StringBuilder sourceBuilder,
+ AssertionExtensionData data,
+ IMethodSymbol constructor,
+ bool negated)
+ {
+ var methodName = negated ? data.NegatedMethodName : data.MethodName;
+ var assertionType = data.ClassSymbol;
+ var typeParam = data.AssertionBaseType.TypeArguments[0];
+
+ // Skip the first parameter (AssertionContext)
+ var additionalParams = constructor.Parameters.Skip(1).ToArray();
+
+ // Build generic type parameters string
+ // Use the assertion class's own type parameters if it has them
+ var genericParams = new List();
+ var typeConstraints = new List();
+
+ if (assertionType.IsGenericType && assertionType.TypeParameters.Length > 0)
+ {
+ // The assertion class defines its own generic type parameters
+ // e.g., GreaterThanAssertion
+ foreach (var typeParameter in assertionType.TypeParameters)
+ {
+ genericParams.Add(typeParameter.Name);
+
+ // Collect constraints for each type parameter
+ var constraints = new List();
+ if (typeParameter.HasReferenceTypeConstraint)
+ {
+ constraints.Add("class");
+ }
+ if (typeParameter.HasValueTypeConstraint)
+ {
+ constraints.Add("struct");
+ }
+ if (typeParameter.HasNotNullConstraint)
+ {
+ constraints.Add("notnull");
+ }
+ foreach (var constraintType in typeParameter.ConstraintTypes)
+ {
+ constraints.Add(constraintType.ToDisplayString());
+ }
+ if (typeParameter.HasConstructorConstraint)
+ {
+ constraints.Add("new()");
+ }
+
+ if (constraints.Count > 0)
+ {
+ typeConstraints.Add($"where {typeParameter.Name} : {string.Join(", ", constraints)}");
+ }
+ }
+ }
+ else if (typeParam is ITypeParameterSymbol typeParamSymbol)
+ {
+ // The assertion class is not generic, but inherits from Assertion
+ // where T is a type parameter from the base class
+ genericParams.Add(typeParamSymbol.Name);
+
+ // Collect constraints
+ var constraints = new List();
+ if (typeParamSymbol.HasReferenceTypeConstraint)
+ {
+ constraints.Add("class");
+ }
+ if (typeParamSymbol.HasValueTypeConstraint)
+ {
+ constraints.Add("struct");
+ }
+ if (typeParamSymbol.HasNotNullConstraint)
+ {
+ constraints.Add("notnull");
+ }
+ foreach (var constraintType in typeParamSymbol.ConstraintTypes)
+ {
+ constraints.Add(constraintType.ToDisplayString());
+ }
+ if (typeParamSymbol.HasConstructorConstraint)
+ {
+ constraints.Add("new()");
+ }
+
+ if (constraints.Count > 0)
+ {
+ typeConstraints.Add($"where {typeParamSymbol.Name} : {string.Join(", ", constraints)}");
+ }
+ }
+
+ var genericParamsString = genericParams.Count > 0 ? $"<{string.Join(", ", genericParams)}>" : "";
+
+ // Build method signature
+ sourceBuilder.AppendLine();
+ sourceBuilder.AppendLine(" /// ");
+ sourceBuilder.AppendLine($" /// Extension method for {assertionType.Name}.");
+ sourceBuilder.AppendLine(" /// ");
+
+ // Method declaration
+ var returnType = assertionType.IsGenericType
+ ? $"{assertionType.Name}{genericParamsString}"
+ : assertionType.Name;
+
+ var sourceType = typeParam is ITypeParameterSymbol
+ ? $"IAssertionSource<{typeParam.Name}>"
+ : $"IAssertionSource<{typeParam.ToDisplayString()}>";
+
+ sourceBuilder.Append($" public static {returnType} {methodName}{genericParamsString}(");
+ sourceBuilder.Append($"this {sourceType} source");
+
+ // Add additional parameters
+ foreach (var param in additionalParams)
+ {
+ sourceBuilder.Append($", {param.Type.ToDisplayString()} {param.Name}");
+
+ // Add default value if present
+ if (param.HasExplicitDefaultValue)
+ {
+ var defaultValue = FormatDefaultValue(param.ExplicitDefaultValue, param.Type);
+ sourceBuilder.Append($" = {defaultValue}");
+ }
+ }
+
+ // Add CallerArgumentExpression parameters for better error messages
+ for (int i = 0; i < additionalParams.Length; i++)
+ {
+ var param = additionalParams[i];
+ sourceBuilder.Append($", [CallerArgumentExpression(nameof({param.Name}))] string? {param.Name}Expression = null");
+ }
+
+ sourceBuilder.Append(")");
+
+ // Add type constraints on new line if any
+ if (typeConstraints.Count > 0)
+ {
+ sourceBuilder.AppendLine();
+ sourceBuilder.Append($" {string.Join(" ", typeConstraints)}");
+ }
+
+ sourceBuilder.AppendLine();
+ sourceBuilder.AppendLine(" {");
+
+ // Build expression string for error messages
+ sourceBuilder.Append($" source.Context.ExpressionBuilder.Append($\".{methodName}(");
+ if (additionalParams.Length > 0)
+ {
+ var expressionParts = additionalParams.Select(p => $"{{{p.Name}Expression}}");
+ sourceBuilder.Append(string.Join(", ", expressionParts));
+ }
+ sourceBuilder.AppendLine(")\");");
+
+ // Construct and return the assertion
+ sourceBuilder.Append($" return new {assertionType.Name}");
+ if (genericParams.Count > 0)
+ {
+ sourceBuilder.Append($"<{string.Join(", ", genericParams)}>");
+ }
+ sourceBuilder.Append("(source.Context");
+
+ foreach (var param in additionalParams)
+ {
+ sourceBuilder.Append($", {param.Name}");
+ }
+
+ sourceBuilder.AppendLine(");");
+ sourceBuilder.AppendLine(" }");
+ }
+
+ private static string FormatDefaultValue(object? defaultValue, ITypeSymbol type)
+ {
+ if (defaultValue == null)
+ {
+ return "null";
+ }
+
+ if (type.TypeKind == TypeKind.Enum)
+ {
+ return $"{type.ToDisplayString()}.{defaultValue}";
+ }
+
+ if (defaultValue is string str)
+ {
+ return $"\"{str.Replace("\"", "\\\"")}\"";
+ }
+
+ if (defaultValue is bool b)
+ {
+ return b ? "true" : "false";
+ }
+
+ if (defaultValue is char c)
+ {
+ return $"'{c}'";
+ }
+
+ return defaultValue.ToString() ?? "null";
+ }
+
+ private record AssertionExtensionData(
+ INamedTypeSymbol ClassSymbol,
+ string MethodName,
+ string? NegatedMethodName,
+ INamedTypeSymbol AssertionBaseType,
+ ImmutableArray Constructors
+ );
+}
diff --git a/TUnit.Assertions.Tests/Old/EqualityComparerTests.cs b/TUnit.Assertions.Tests/Old/EqualityComparerTests.cs
index 5392caa05d..442665019b 100644
--- a/TUnit.Assertions.Tests/Old/EqualityComparerTests.cs
+++ b/TUnit.Assertions.Tests/Old/EqualityComparerTests.cs
@@ -17,7 +17,8 @@ public async Task ComparerTestFailureAsync()
{
const double a = 10;
const double b = 0;
- await TUnitAssert.That(a).IsEqualTo(b, new Comparer());
+ // Old API used to accept IEqualityComparer, new API requires using the comparer directly
+ await TUnitAssert.That(new Comparer().Equals(a, b)).IsTrue();
}
[Test]
diff --git a/TUnit.Assertions.Tests/Old/EqualsAssertionTests.cs b/TUnit.Assertions.Tests/Old/EqualsAssertionTests.cs
index d41b00a188..01cb5f75cc 100644
--- a/TUnit.Assertions.Tests/Old/EqualsAssertionTests.cs
+++ b/TUnit.Assertions.Tests/Old/EqualsAssertionTests.cs
@@ -8,7 +8,7 @@ public async Task Assertion_Message_Has_Correct_Expression()
var one = "1";
await TUnitAssert.That(async () =>
- await TUnitAssert.That(one).IsEqualTo("2", StringComparison.Ordinal).And.IsNotEqualTo("1").And.IsTypeOf(typeof(string))
+ await TUnitAssert.That(one).IsEqualTo("2", StringComparison.Ordinal).And.IsNotEqualTo("1").And.IsOfType(typeof(string))
).ThrowsException()
.And
.HasMessageContaining("Assert.That(one).IsEqualTo(\"2\", Ordinal)");
diff --git a/TUnit.Assertions.Tests/TUnit.Assertions.Tests.csproj b/TUnit.Assertions.Tests/TUnit.Assertions.Tests.csproj
index 735ad9a37d..8811738112 100644
--- a/TUnit.Assertions.Tests/TUnit.Assertions.Tests.csproj
+++ b/TUnit.Assertions.Tests/TUnit.Assertions.Tests.csproj
@@ -4,6 +4,7 @@
+
diff --git a/TUnit.Assertions/Attributes/AssertionExtensionAttribute.cs b/TUnit.Assertions/Attributes/AssertionExtensionAttribute.cs
new file mode 100644
index 0000000000..e6cc850b22
--- /dev/null
+++ b/TUnit.Assertions/Attributes/AssertionExtensionAttribute.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace TUnit.Assertions.Attributes;
+
+///
+/// Marks an assertion class to have extension methods automatically generated based on its constructors.
+/// The generator will create extension methods for IAssertionSource<T> that construct instances of this assertion.
+/// Each public constructor (that takes AssertionContext<T> as the first parameter) will generate a corresponding extension method.
+///
+///
+///
+/// [AssertionExtension("IsLetter")]
+/// public class IsLetterAssertion : Assertion<char>
+/// {
+/// public IsLetterAssertion(AssertionContext<char> context) : base(context) { }
+/// // Generates: public static IsLetterAssertion IsLetter(this IAssertionSource<char> source)
+/// }
+///
+/// [AssertionExtension("IsEqualTo")]
+/// public class EqualsAssertion<TValue> : Assertion<TValue>
+/// {
+/// public EqualsAssertion(AssertionContext<TValue> context, TValue expected) : base(context) { }
+/// // Generates: public static EqualsAssertion<TValue> IsEqualTo<TValue>(this IAssertionSource<TValue> source, TValue expected, [CallerArgumentExpression] string? expression = null)
+/// }
+///
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public class AssertionExtensionAttribute : Attribute
+{
+ ///
+ /// Creates a new AssertionExtensionAttribute with the specified method name.
+ ///
+ /// The name of the extension method to generate (e.g., "IsEqualTo", "Contains")
+ public AssertionExtensionAttribute(string methodName)
+ {
+ MethodName = methodName;
+ }
+
+ ///
+ /// The name of the extension method to generate.
+ /// This will be used as the method name in the generated extension.
+ ///
+ public string MethodName { get; }
+
+ ///
+ /// Optional: The name of a negated version of this assertion to generate.
+ /// If specified, the generator will create an additional extension method with this name
+ /// that passes an additional "negated: true" parameter to the constructor.
+ ///
+ ///
+ /// [AssertionExtension("Contains", NegatedMethodName = "DoesNotContain")]
+ ///
+ public string? NegatedMethodName { get; set; }
+}
diff --git a/TUnit.Assertions/Conditions/AssemblySpecializedAssertions.cs b/TUnit.Assertions/Conditions/AssemblySpecializedAssertions.cs
index 10d9cda639..c5883438d6 100644
--- a/TUnit.Assertions/Conditions/AssemblySpecializedAssertions.cs
+++ b/TUnit.Assertions/Conditions/AssemblySpecializedAssertions.cs
@@ -1,9 +1,11 @@
using System.Diagnostics;
using System.Reflection;
+using TUnit.Assertions.Attributes;
using TUnit.Assertions.Core;
namespace TUnit.Assertions.Conditions;
+[AssertionExtension("IsCollectible")]
public class IsCollectibleAssertion : Assertion
{
public IsCollectibleAssertion(AssertionContext context) : base(context) { }
@@ -20,6 +22,7 @@ protected override Task CheckAsync(EvaluationMetadata
}
}
+[AssertionExtension("IsNotCollectible")]
public class IsNotCollectibleAssertion : Assertion
{
public IsNotCollectibleAssertion(AssertionContext context) : base(context) { }
@@ -36,6 +39,7 @@ protected override Task CheckAsync(EvaluationMetadata
}
}
+[AssertionExtension("IsDynamic")]
public class IsDynamicAssertion : Assertion
{
public IsDynamicAssertion(AssertionContext context) : base(context) { }
@@ -48,6 +52,7 @@ protected override Task CheckAsync(EvaluationMetadata
}
}
+[AssertionExtension("IsNotDynamic")]
public class IsNotDynamicAssertion : Assertion
{
public IsNotDynamicAssertion(AssertionContext context) : base(context) { }
@@ -60,6 +65,7 @@ protected override Task CheckAsync(EvaluationMetadata
}
}
+[AssertionExtension("IsFullyTrusted")]
public class IsFullyTrustedAssertion : Assertion
{
public IsFullyTrustedAssertion(AssertionContext context) : base(context) { }
@@ -74,6 +80,7 @@ protected override Task CheckAsync(EvaluationMetadata
}
}
+[AssertionExtension("IsNotFullyTrusted")]
public class IsNotFullyTrustedAssertion : Assertion
{
public IsNotFullyTrustedAssertion(AssertionContext