nunit.analyzers
nunit.analyzers copied to clipboard
Replace UpdateStringFormatToFormattableString with String.Format
Consider the following (mostly similar) tests:
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInStandardOrder1()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(""expected"", ""actual"", ""{0}"", ""first"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(""actual"", Is.EqualTo(""expected""), $""{""first""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInStandardOrder2()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(""expected"", ""actual"", ""{0}"", new[] { ""first"" });
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(""actual"", Is.EqualTo(""expected""), $""{""first""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInStandardOrder3()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
var args = new[] { ""first"", ""second"" };
↓ClassicAssert.AreEqual(""expected"", ""actual"", ""{0}, {1}"", args);
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
var args = new[] { ""first"", ""second"" };
Assert.That(""actual"", Is.EqualTo(""expected""), $""{""first""}, {""second""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInStandardOrder4()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(""expected"", ""actual"", ""{0}, {1}"", ""first"", ""second"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(""actual"", Is.EqualTo(""expected""), $""{""first""}, {""second""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInStandardOrder5()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(""expected"", ""actual"", ""{0}, {1}"", new[] { ""first"", ""second"" });
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(""actual"", Is.EqualTo(""expected""), $""{""first""}, {""second""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInNonstandardOrder1()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(args: ""first"", actual: ""actual"", message: ""{0}"", expected: ""expected"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(actual: ""actual"", Is.EqualTo(expected: ""expected""), $""{""first""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInNonstandardOrder2()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(args: new[] { ""first"" }, actual: ""actual"", message: ""{0}"", expected: ""expected"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(actual: ""actual"", Is.EqualTo(expected: ""expected""), $""{""first""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInNonstandardOrder3()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(args: new[] { ""first"", ""second"" }, actual: ""actual"", message: ""{0}, {1}"", expected: ""expected"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(actual: ""actual"", Is.EqualTo(expected: ""expected""), $""{""first""}, {""second""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
[Test]
public void VerifyAreEqualFixWithMessageAndParamsInNonstandardOrder4()
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
↓ClassicAssert.AreEqual(args: new[] { ""first"", ""second"", ""third"" }, actual: ""actual"", message: ""{0}, {1}, {2}"", expected: ""expected"");
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void TestMethod()
{
Assert.That(actual: ""actual"", Is.EqualTo(expected: ""expected""), $""{""first""}, {""second""}, {""third""}"");
}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode, fixTitle: ClassicModelAssertUsageCodeFix.TransformToConstraintModelDescription);
}
Some of these fail, even after #716. In order to fix InStandardOrder3
, in particular, I believe we should replace GetInterpolatedMessageArgumentOrDefault
with String.Format
:
var stringFormat = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName("String"),
SyntaxFactory.IdentifierName("Format"));
var argumentList = SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[] { messageArgument.WithNameColon(null) }.Concat(
args.SelectMany((arg, _) => new SyntaxNodeOrToken[] { SyntaxFactory.Token(SyntaxKind.CommaToken), arg.WithNameColon(null) }))));
return SyntaxFactory.Argument(SyntaxFactory.InvocationExpression(stringFormat, argumentList));
I think this will be come truly startable once https://github.com/dotnet/roslyn/issues/68469 is done and released.
@Bartleby2718 The difference between an InterpolatedString
and String.Format
is that the first is lazy, i.e. the string is only formatted when the test fails. The latter will always be formatted before the test is run, making the test call slower.
For completeness your tests are valid and in case a NUnit test actually uses an args
array, instead of separate arguments, we cannot even convert it if the args
array is not-created in-line but in a separate statement.
So yes we would need to convert:
object[] args = new[] { ""first"", ""second"" };
ClassicAssert.AreEqual(args: args, actual: ""actual"", message: ""{0}, {1}"", expected: ""expected"");
into:
object[] args = new[] { ""first"", ""second"" };
Assert.That(actual: ""actual"", Is.EqualTo(expected: ""expected""), () => string.Format(""{0}, {1}"", args);
Note the addition of () =>
to make the call lazy.
Good to know that Func<string> getExceptionMessage
is supported in the constraint model!
This issue is not just limited to 'named:' arguments, the current code will fail if the parameters for the format string are in an array.
[TestCase(3.0, 4)]
public void Test(object actual, object expected)
{
object[] args = { expected, actual };
Assert.AreEqual(expected, actual, "Expected: {0} Got: {1}", args);
}
The AreEqual
gets converted into:
Assert.AreEqual(expected, actual, "Expected: {0} Got: {1}", args);
But should be converted into:
Assert.That(actual, Is.EqualTo(expected), () => string.Format("Expected: {0} Got: {1}", args));
Similar the following fails:
ClassicAssert.AreEqual(expected, actual, GetLocalizedFormatSpecification(), expected, actual);
I have implemented this on top of #716 and will rebase when that is merged.