diff --git a/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs b/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs index 4914f8aae6..2701ebbe30 100644 --- a/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs +++ b/TUnit.Assertions.Analyzers.CodeFixers.Tests/XUnitAssertionCodeFixProviderTests.cs @@ -219,4 +219,204 @@ public class User """ ); } + + [Test] + public async Task Xunit_True_Without_Message() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = true; + {|#0:Xunit.Assert.True(result)|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = true; + Assert.That(result).IsTrue(); + } + } + """ + ); + } + + [Test] + public async Task Xunit_True_With_Message() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = true; + {|#0:Xunit.Assert.True(result, "user message if false")|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = true; + Assert.That(result).IsTrue().Because("user message if false"); + } + } + """ + ); + } + + [Test] + public async Task Xunit_False_Without_Message() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = false; + {|#0:Xunit.Assert.False(result)|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = false; + Assert.That(result).IsFalse(); + } + } + """ + ); + } + + [Test] + public async Task Xunit_False_With_Message() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = false; + {|#0:Xunit.Assert.False(result, "user message if true")|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + bool result = false; + Assert.That(result).IsFalse().Because("user message if true"); + } + } + """ + ); + } + + [Test] + public async Task Xunit_Fail_With_Message() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + {|#0:Xunit.Assert.Fail("test failure message")|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + Fail.Test("test failure message"); + } + } + """ + ); + } + + [Test] + public async Task Xunit_Skip_With_Reason() + { + await Verifier + .VerifyCodeFixAsync( + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + {|#0:Xunit.Assert.Skip("skipping because of reason")|}; + } + } + """, + Verifier.Diagnostic(Rules.XUnitAssertion) + .WithLocation(0), + """ + using System.Threading.Tasks; + + public class MyClass + { + public void MyTest() + { + Skip.Test("skipping because of reason"); + } + } + """ + ); + } } diff --git a/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs b/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs index 8afa092b20..e5a5f6cf60 100644 --- a/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs +++ b/TUnit.Assertions.Analyzers.CodeFixers/XUnitAssertionCodeFixProvider.cs @@ -159,12 +159,12 @@ private static async Task ConvertAssertionAsync(CodeFixContext context : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNull()"), "True" => isInSatisfy && parameterName != null - ? SyntaxFactory.ParseExpression($"{actual}.IsTrue()") - : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsTrue()"), + ? SyntaxFactory.ParseExpression($"{expected}.IsTrue()") + : CreateBooleanAssertion(expected, argumentListArguments, "IsTrue"), "False" => isInSatisfy && parameterName != null - ? SyntaxFactory.ParseExpression($"{actual}.IsFalse()") - : SyntaxFactory.ParseExpression($"Assert.That({actual}).IsFalse()"), + ? SyntaxFactory.ParseExpression($"{expected}.IsFalse()") + : CreateBooleanAssertion(expected, argumentListArguments, "IsFalse"), "Same" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsSameReferenceAs({expected})"), @@ -202,9 +202,9 @@ private static async Task ConvertAssertionAsync(CodeFixContext context "NotEmpty" => SyntaxFactory.ParseExpression($"Assert.That({actual}).IsNotEmpty()"), - "Fail" => SyntaxFactory.ParseExpression("Fail.Test()"), + "Fail" => SyntaxFactory.ParseExpression($"Fail.Test({expected})"), - "Skip" => SyntaxFactory.ParseExpression("Skip.Test()"), + "Skip" => SyntaxFactory.ParseExpression($"Skip.Test({expected})"), "Throws" or "ThrowsAsync" => isGeneric ? SyntaxFactory.ParseExpression($"Assert.That({actual}).ThrowsExactly<{genericArgs}>()") @@ -315,6 +315,22 @@ private static bool IsEnumerable(ITypeSymbol typeSymbol) return typeSymbol.AllInterfaces.Any(i => i.GloballyQualified() == "global::System.Collections.IEnumerable"); } + private static ExpressionSyntax CreateBooleanAssertion( + ArgumentSyntax? condition, + SeparatedSyntaxList argumentListArguments, + string assertionMethod) + { + // Check if there's a user message (second argument) + var userMessage = argumentListArguments.ElementAtOrDefault(1); + + if (userMessage != null) + { + return SyntaxFactory.ParseExpression($"Assert.That({condition}).{assertionMethod}().Because({userMessage})"); + } + + return SyntaxFactory.ParseExpression($"Assert.That({condition}).{assertionMethod}()"); + } + private static async Task Contains(CodeFixContext context, MemberAccessExpressionSyntax memberAccessExpressionSyntax, ArgumentSyntax? actual, ArgumentSyntax? expected) { @@ -508,8 +524,12 @@ argumentList.Parent is InvocationExpressionSyntax invocation && SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsNotNull()"), "Null" when args.Count >= 1 => SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsNull()"), + "True" when args.Count >= 2 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsTrue().Because({args[1]})"), "True" when args.Count >= 1 => SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsTrue()"), + "False" when args.Count >= 2 => + SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsFalse().Because({args[1]})"), "False" when args.Count >= 1 => SyntaxFactory.ParseExpression($"Assert.That({args[0]}).IsFalse()"), "Equal" when args.Count >= 2 =>