Skip to content

Commit d7aa43d

Browse files
thomhurstclaude
andcommitted
feat(migration): add Does.Match and Does.Not.Match constraint support
- Add Does.Match to convert to Matches() - Add Does.Not.Match to convert to DoesNotMatch() - Add Is.Matches to with-message version (was only in non-message) - Add tests for Does.Not.EndWith, Does.Match, Does.Not.Match patterns Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 613c797 commit d7aa43d

2 files changed

Lines changed: 120 additions & 4 deletions

File tree

TUnit.Analyzers.CodeFixers/NUnitMigrationCodeFixProvider.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ private ExpressionSyntax ConvertConstraintToTUnitWithMessage(ExpressionSyntax ac
662662
};
663663
}
664664

665-
// Handle Does.Not.StartWith, Does.Not.EndWith, Does.Not.Contain
665+
// Handle Does.Not.StartWith, Does.Not.EndWith, Does.Not.Contain, Does.Not.Match
666666
if (memberAccess.Expression is MemberAccessExpressionSyntax doesNotAccess &&
667667
doesNotAccess.Expression is IdentifierNameSyntax { Identifier.Text: "Does" } &&
668668
doesNotAccess.Name.Identifier.Text == "Not")
@@ -672,6 +672,7 @@ private ExpressionSyntax ConvertConstraintToTUnitWithMessage(ExpressionSyntax ac
672672
"StartWith" => CreateTUnitAssertionWithMessage("DoesNotStartWith", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
673673
"EndWith" => CreateTUnitAssertionWithMessage("DoesNotEndWith", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
674674
"Contain" => CreateTUnitAssertionWithMessage("DoesNotContain", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
675+
"Match" => CreateTUnitAssertionWithMessage("DoesNotMatch", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
675676
_ => CreateTUnitAssertionWithMessage("DoesNotContain", actualValue, message, constraint.ArgumentList.Arguments.ToArray())
676677
};
677678
}
@@ -704,13 +705,14 @@ private ExpressionSyntax ConvertConstraintToTUnitWithMessage(ExpressionSyntax ac
704705
};
705706
}
706707

707-
// Handle Does.StartWith, Does.EndWith, Contains.Substring
708+
// Handle Does.StartWith, Does.EndWith, Does.Match, Contains.Substring
708709
if (memberAccess.Expression is IdentifierNameSyntax { Identifier.Text: "Does" or "Contains" })
709710
{
710711
return methodName switch
711712
{
712713
"StartWith" => CreateTUnitAssertionWithMessage("StartsWith", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
713714
"EndWith" => CreateTUnitAssertionWithMessage("EndsWith", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
715+
"Match" => CreateTUnitAssertionWithMessage("Matches", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
714716
"Substring" => CreateTUnitAssertionWithMessage("Contains", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
715717
_ => CreateTUnitAssertionWithMessage("IsEqualTo", actualValue, message, SyntaxFactory.Argument(constraint))
716718
};
@@ -729,6 +731,7 @@ private ExpressionSyntax ConvertConstraintToTUnitWithMessage(ExpressionSyntax ac
729731
"SameAs" => CreateTUnitAssertionWithMessage("IsSameReferenceAs", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
730732
"InstanceOf" => CreateTUnitAssertionWithMessage("IsAssignableTo", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
731733
"TypeOf" => CreateTUnitAssertionWithMessage("IsTypeOf", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
734+
"Matches" => CreateTUnitAssertionWithMessage("Matches", actualValue, message, constraint.ArgumentList.Arguments.ToArray()),
732735
_ => CreateTUnitAssertionWithMessage("IsEqualTo", actualValue, message, SyntaxFactory.Argument(constraint))
733736
};
734737
}
@@ -877,7 +880,7 @@ private ExpressionSyntax ConvertConstraintToTUnit(ExpressionSyntax actualValue,
877880
};
878881
}
879882

880-
// Handle Does.Not.StartWith, Does.Not.EndWith, Does.Not.Contain
883+
// Handle Does.Not.StartWith, Does.Not.EndWith, Does.Not.Contain, Does.Not.Match
881884
if (memberAccess.Expression is MemberAccessExpressionSyntax doesNotAccess &&
882885
doesNotAccess.Expression is IdentifierNameSyntax { Identifier.Text: "Does" } &&
883886
doesNotAccess.Name.Identifier.Text == "Not")
@@ -887,6 +890,7 @@ private ExpressionSyntax ConvertConstraintToTUnit(ExpressionSyntax actualValue,
887890
"StartWith" => CreateTUnitAssertion("DoesNotStartWith", actualValue, constraint.ArgumentList.Arguments.ToArray()),
888891
"EndWith" => CreateTUnitAssertion("DoesNotEndWith", actualValue, constraint.ArgumentList.Arguments.ToArray()),
889892
"Contain" => CreateTUnitAssertion("DoesNotContain", actualValue, constraint.ArgumentList.Arguments.ToArray()),
893+
"Match" => CreateTUnitAssertion("DoesNotMatch", actualValue, constraint.ArgumentList.Arguments.ToArray()),
890894
_ => CreateTUnitAssertion("DoesNotContain", actualValue, constraint.ArgumentList.Arguments.ToArray())
891895
};
892896
}
@@ -901,13 +905,14 @@ private ExpressionSyntax ConvertConstraintToTUnit(ExpressionSyntax actualValue,
901905
};
902906
}
903907

904-
// Handle Does.StartWith, Does.EndWith, Contains.Substring
908+
// Handle Does.StartWith, Does.EndWith, Does.Match, Contains.Substring
905909
if (memberAccess.Expression is IdentifierNameSyntax { Identifier.Text: "Does" or "Contains" })
906910
{
907911
return methodName switch
908912
{
909913
"StartWith" => CreateTUnitAssertion("StartsWith", actualValue, constraint.ArgumentList.Arguments.ToArray()),
910914
"EndWith" => CreateTUnitAssertion("EndsWith", actualValue, constraint.ArgumentList.Arguments.ToArray()),
915+
"Match" => CreateTUnitAssertion("Matches", actualValue, constraint.ArgumentList.Arguments.ToArray()),
911916
"Substring" => CreateTUnitAssertion("Contains", actualValue, constraint.ArgumentList.Arguments.ToArray()),
912917
_ => CreateTUnitAssertion("IsEqualTo", actualValue, SyntaxFactory.Argument(constraint))
913918
};

TUnit.Analyzers.Tests/NUnitMigrationAnalyzerTests.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,117 @@ public async Task TestMethod()
886886
);
887887
}
888888

889+
[Test]
890+
public async Task NUnit_Does_Not_EndWith_Converted()
891+
{
892+
await CodeFixer.VerifyCodeFixAsync(
893+
"""
894+
using NUnit.Framework;
895+
896+
{|#0:public class MyClass|}
897+
{
898+
[Test]
899+
public void TestMethod()
900+
{
901+
Assert.That("hello world", Does.Not.EndWith("foo"));
902+
}
903+
}
904+
""",
905+
Verifier.Diagnostic(Rules.NUnitMigration).WithLocation(0),
906+
"""
907+
using System.Threading.Tasks;
908+
using TUnit.Core;
909+
using TUnit.Assertions;
910+
using static TUnit.Assertions.Assert;
911+
using TUnit.Assertions.Extensions;
912+
913+
public class MyClass
914+
{
915+
[Test]
916+
public async Task TestMethod()
917+
{
918+
await Assert.That("hello world").DoesNotEndWith("foo");
919+
}
920+
}
921+
""",
922+
ConfigureNUnitTest
923+
);
924+
}
925+
926+
[Test]
927+
public async Task NUnit_Does_Match_Converted()
928+
{
929+
await CodeFixer.VerifyCodeFixAsync(
930+
"""
931+
using NUnit.Framework;
932+
933+
{|#0:public class MyClass|}
934+
{
935+
[Test]
936+
public void TestMethod()
937+
{
938+
Assert.That("hello123", Does.Match(@"\d+"));
939+
}
940+
}
941+
""",
942+
Verifier.Diagnostic(Rules.NUnitMigration).WithLocation(0),
943+
"""
944+
using System.Threading.Tasks;
945+
using TUnit.Core;
946+
using TUnit.Assertions;
947+
using static TUnit.Assertions.Assert;
948+
using TUnit.Assertions.Extensions;
949+
950+
public class MyClass
951+
{
952+
[Test]
953+
public async Task TestMethod()
954+
{
955+
await Assert.That("hello123").Matches(@"\d+");
956+
}
957+
}
958+
""",
959+
ConfigureNUnitTest
960+
);
961+
}
962+
963+
[Test]
964+
public async Task NUnit_Does_Not_Match_Converted()
965+
{
966+
await CodeFixer.VerifyCodeFixAsync(
967+
"""
968+
using NUnit.Framework;
969+
970+
{|#0:public class MyClass|}
971+
{
972+
[Test]
973+
public void TestMethod()
974+
{
975+
Assert.That("hello", Does.Not.Match(@"\d+"));
976+
}
977+
}
978+
""",
979+
Verifier.Diagnostic(Rules.NUnitMigration).WithLocation(0),
980+
"""
981+
using System.Threading.Tasks;
982+
using TUnit.Core;
983+
using TUnit.Assertions;
984+
using static TUnit.Assertions.Assert;
985+
using TUnit.Assertions.Extensions;
986+
987+
public class MyClass
988+
{
989+
[Test]
990+
public async Task TestMethod()
991+
{
992+
await Assert.That("hello").DoesNotMatch(@"\d+");
993+
}
994+
}
995+
""",
996+
ConfigureNUnitTest
997+
);
998+
}
999+
8891000
[Test]
8901001
public async Task NUnit_AssertionMessage_Converted_To_Because()
8911002
{

0 commit comments

Comments
 (0)