Skip to content

Commit

Permalink
Merge pull request #721 from manfred-brands/issue719_NUnit2050_Assume
Browse files Browse the repository at this point in the history
Add support for Assume.That for NUnit2050
  • Loading branch information
manfred-brands authored Apr 9, 2024
2 parents cbe1e2b + 5a7a2ca commit ea11a27
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 56 deletions.
12 changes: 12 additions & 0 deletions documentation/NUnit2050.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ Here what you think are parameters to a format specification are actually interp
the _actual_ and _constraint_ expression strings.
Unfortunately you only find that out when the test fails, which could be never.

The affected methods are:

```csharp
Assert.Pass
Assert.Fail
Assert.Warn
Assert.Ignore
Assert.Inconclusive
Assert.That
Assume.That
```

## How to fix violations

The following code, valid in NUnit3:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public sealed class UpdateStringFormatToInterpolatableStringAnalyzerTests
private readonly DiagnosticAnalyzer analyzer = new UpdateStringFormatToInterpolatableStringAnalyzer();
private readonly ExpectedDiagnostic diagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.UpdateStringFormatToInterpolatableString);

private static readonly string[] AssertAndAssume = new[]
{
NUnitFrameworkConstants.NameOfAssert,
NUnitFrameworkConstants.NameOfAssume,
};

private static IEnumerable<string> NoArgumentsAsserts { get; } = new[]
{
NUnitFrameworkConstants.NameOfAssertPass,
Expand Down Expand Up @@ -72,93 +78,93 @@ public void AnalyzeWhenFormattableStringIsUsed(string method)
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertBoolWhenNoArgumentsAreUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeBoolWhenNoArgumentsAreUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
Assert.That(true);
{assertOrAssume}.That(true);
");
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertBoolWhenOnlyMessageArgumentIsUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeBoolWhenOnlyMessageArgumentIsUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
Assert.That(false, ""Message"");
{assertOrAssume}.That(false, ""Message"");
");
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertBoolWhenFormatAndArgumentsAreUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeBoolWhenFormatAndArgumentsAreUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
Assert.That(false, ""Method: {{0}}"", false.ToString());
{assertOrAssume}.That(false, ""Method: {{0}}"", false.ToString());
");
RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode);
}

[Test]
public void AnalyzeAssertBoolWhenFormattableStringIsUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeBoolWhenFormattableStringIsUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
Assert.That(false, $""Method: {{false}}"");
{assertOrAssume}.That(false, $""Method: {{false}}"");
");
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertThatWhenNoArgumentsAreUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeThatWhenNoArgumentsAreUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
double pi = 3.1415;
Assert.That(pi, Is.EqualTo(3.1415).Within(0.0001));
{assertOrAssume}.That(pi, Is.EqualTo(3.1415).Within(0.0001));
");
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertThatWhenOnlyMessageArgumentIsUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeThatWhenOnlyMessageArgumentIsUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
double pi = 3.1415;
Assert.That(pi, Is.EqualTo(3.1415).Within(0.0001), ""Message"");
{assertOrAssume}.That(pi, Is.EqualTo(3.1415).Within(0.0001), ""Message"");
");
RoslynAssert.Valid(this.analyzer, testCode);
}

[Test]
public void AnalyzeAssertThatWhenFormatAndStringArgumentsAreUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeThatWhenFormatAndStringArgumentsAreUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
var testCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@$"
[TestCase(""NUnit 4.0"", ""NUnit 3.14"")]
public void AssertSomething(string actual, string expected)
{
Assert.That(actual, Is.EqualTo(expected).IgnoreCase, ""Expected '{0}', but got: {1}"", expected, actual);
}");
public void {assertOrAssume}Something(string actual, string expected)
{{
{assertOrAssume}.That(actual, Is.EqualTo(expected).IgnoreCase, ""Expected '{{0}}', but got: {{1}}"", expected, actual);
}}");

RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode);
}

#if !NUNIT4
[Test]
public void AnalyzeAssertThatWhenFormatAndArgumentsAreUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeThatWhenFormatAndArgumentsAreUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
double pi = 3.1415;
Assert.That(pi, Is.EqualTo(3.1415).Within(0.0001), ""Method: {{0}}"", pi);
{assertOrAssume}.That(pi, Is.EqualTo(3.1415).Within(0.0001), ""Method: {{0}}"", pi);
");
RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode);
}
#endif

[Test]
public void AnalyzeAssertThatWhenFormatStringIsUsed()
[TestCaseSource(nameof(AssertAndAssume))]
public void AnalyzeAssertOrAssumeThatWhenFormatStringIsUsed(string assertOrAssume)
{
var testCode = TestUtility.WrapInTestMethod(@$"
double pi = 3.1415;
Assert.That(pi, Is.EqualTo(3.1415).Within(0.0001), $""Method: {{pi}}"");
{assertOrAssume}.That(pi, Is.EqualTo(3.1415).Within(0.0001), $""Method: {{pi}}"");
");
RoslynAssert.Valid(this.analyzer, testCode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ public sealed class UpdateStringFormatToInterpolatableStringCodeFixTests
private static readonly ExpectedDiagnostic expectedDiagnostic =
ExpectedDiagnostic.Create(AnalyzerIdentifiers.UpdateStringFormatToInterpolatableString);

private static readonly string[] AssertAndAssume = new[]
{
NUnitFrameworkConstants.NameOfAssert,
NUnitFrameworkConstants.NameOfAssume,
};

[Test]
public void VerifyGetFixableDiagnosticIds()
{
Expand All @@ -24,21 +30,21 @@ public void VerifyGetFixableDiagnosticIds()
}

#if NUNIT4
[Test]
public void AccidentallyUseFormatSpecification()
[TestCaseSource(nameof(AssertAndAssume))]
public void AccidentallyUseFormatSpecification(string assertOrAssume)
{
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
var code = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@$"
[TestCase(""NUnit 4.0"", ""NUnit 3.14"")]
public void AssertSomething(string actual, string expected)
{
Assert.That(actual, Is.EqualTo(expected).IgnoreCase, ""Expected '{0}', but got: {1}"", expected, actual);
}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@"
public void {assertOrAssume}Something(string actual, string expected)
{{
{assertOrAssume}.That(actual, Is.EqualTo(expected).IgnoreCase, ""Expected '{{0}}', but got: {{1}}"", expected, actual);
}}");
var fixedCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings(@$"
[TestCase(""NUnit 4.0"", ""NUnit 3.14"")]
public void AssertSomething(string actual, string expected)
{
Assert.That(actual, Is.EqualTo(expected).IgnoreCase, $""Expected '{expected}', but got: {actual}"");
}");
public void {assertOrAssume}Something(string actual, string expected)
{{
{assertOrAssume}.That(actual, Is.EqualTo(expected).IgnoreCase, $""Expected '{{expected}}', but got: {{actual}}"");
}}");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode);
}
#else
Expand All @@ -54,30 +60,30 @@ public void ConvertWhenNoMinimumParameters(string method)
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode);
}

[Test]
public void ConvertWhenMinimumParametersIsOne()
[TestCaseSource(nameof(AssertAndAssume))]
public void ConvertWhenMinimumParametersIsOne(string assertOrAssume)
{
var code = TestUtility.WrapInTestMethod(@"
var code = TestUtility.WrapInTestMethod(@$"
const bool actual = false;
Assert.That(actual, ""Expected actual value to be true, but was: {0}"", actual);
{assertOrAssume}.That(actual, ""Expected actual value to be true, but was: {{0}}"", actual);
");
var fixedCode = TestUtility.WrapInTestMethod(@"
var fixedCode = TestUtility.WrapInTestMethod(@$"
const bool actual = false;
Assert.That(actual, $""Expected actual value to be true, but was: {actual}"");
{assertOrAssume}.That(actual, $""Expected actual value to be true, but was: {{actual}}"");
");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode);
}

[Test]
public void ConvertWhenMinimumParametersIsTwo()
[TestCaseSource(nameof(AssertAndAssume))]
public void ConvertWhenMinimumParametersIsTwo(string assertOrAssume)
{
var code = TestUtility.WrapInTestMethod(@"
var code = TestUtility.WrapInTestMethod(@$"
const int actual = 42;
Assert.That(actual, Is.EqualTo(42), ""Expected actual value to be 42, but was: {0} at time {1:HH:mm:ss}"", actual, DateTime.Now);
{assertOrAssume}.That(actual, Is.EqualTo(42), ""Expected actual value to be 42, but was: {{0}} at time {{1:HH:mm:ss}}"", actual, DateTime.Now);
");
var fixedCode = TestUtility.WrapInTestMethod(@"
var fixedCode = TestUtility.WrapInTestMethod(@$"
const int actual = 42;
Assert.That(actual, Is.EqualTo(42), $""Expected actual value to be 42, but was: {actual} at time {DateTime.Now:HH:mm:ss}"");
{assertOrAssume}.That(actual, Is.EqualTo(42), $""Expected actual value to be 42, but was: {{actual}} at time {{DateTime.Now:HH:mm:ss}}"");
");
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode);
}
Expand Down
7 changes: 7 additions & 0 deletions src/nunit.analyzers/Extensions/ITypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ @this.ContainingAssembly.Name is NUnitFrameworkConstants.NUnitFrameworkAssemblyN
@this.Name is NUnitFrameworkConstants.NameOfAssert;
}

internal static bool IsAssume(this ITypeSymbol? @this)
{
return @this is not null &&
@this.ContainingAssembly.Name is NUnitFrameworkConstants.NUnitFrameworkAssemblyName &&
@this.Name is NUnitFrameworkConstants.NameOfAssume;
}

internal static bool IsClassicAssert(this ITypeSymbol? @this)
{
return @this is not null &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public sealed class UpdateStringFormatToInterpolatableStringAnalyzer : BaseAsser

protected override bool IsAssert(bool hasClassicAssert, IInvocationOperation invocationOperation)
{
return invocationOperation.TargetMethod.ContainingType.IsAssert();
INamedTypeSymbol containingType = invocationOperation.TargetMethod.ContainingType;
return containingType.IsAssert() || containingType.IsAssume();
}

protected override void AnalyzeAssertInvocation(Version nunitVersion, OperationAnalysisContext context, IInvocationOperation assertOperation)
Expand Down

0 comments on commit ea11a27

Please sign in to comment.