forked from nunit/nunit.analyzers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Analyzer/CodeFix for StringAssert
- Loading branch information
1 parent
3b57fd2
commit 1ad9b02
Showing
15 changed files
with
444 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# NUnit2048 | ||
|
||
## Consider using Assert.That(...) instead of StringAssert(...) | ||
|
||
| Topic | Value | ||
| :-- | :-- | ||
| Id | NUnit2048 | ||
| Severity | Warning | ||
| Enabled | True | ||
| Category | Assertion | ||
| Code | [StringAssertUsageAnalyzer](https://github.com/nunit/nunit.analyzers/blob/master/src/nunit.analyzers/StringAssertUsage/StringAssertUsageAnalyzer.cs) | ||
|
||
## Description | ||
|
||
Consider using the constraint model, `Assert.That(actual, {0}(expected))`, instead of the classic model, `StringAssert.{1}(expected, actual)`. | ||
|
||
## Motivation | ||
|
||
The classic Assert model contains less flexibility than the constraint model and makes it easy to mix the `expected` and the `actual` parameter, | ||
so this analyzer marks usages of all `StringAssert` methods from the classic Assert model. | ||
|
||
```csharp | ||
[Test] | ||
public void Test() | ||
{ | ||
StringAssert.Contains(expected, actual); | ||
StringAssert.DoesNotContain(expected, actual); | ||
StringAssert.StartsWith(expected, actual); | ||
StringAssert.DoesNotStartWith(expected, actual); | ||
StringAssert.EndsWith(expected, actual); | ||
StringAssert.DoesNotEndWith(expected, actual); | ||
StringAssert.AreEqualIgnoreCase(expected, actual); | ||
StringAssert.AreNotEqualIgnoreCase(expected, actual); | ||
StringAssert.IsMatch(expected, actual); | ||
StringAssert.DoesNotMatch(expected, actual); | ||
} | ||
``` | ||
|
||
## How to fix violations | ||
|
||
The analyzer comes with a code fix that will replace `StringAssert.<method>(expected, actual)` with | ||
`Assert.That(actual, <constraint>(expected))`. So the code block above will be changed into. | ||
|
||
```csharp | ||
[Test] | ||
public void Test() | ||
{ | ||
Assert.That(actual, Does.Contain(expected)); | ||
Assert.That(actual, Does.Not.Contain(expected)); | ||
Assert.That(actual, Does.StartWith(expected)); | ||
Assert.That(actual, Does.Not.StartWith(expected)); | ||
Assert.That(actual, Does.EndWith(expected)); | ||
Assert.That(actual, Does.Not.EndWith(expected)); | ||
Assert.That(actual, Is.EqualTo(expected).IgnoreCase); | ||
Assert.That(actual, Is.Not.EqualTo(expected).IgnoreCase); | ||
Assert.That(actual, Does.Match(expected)); | ||
Assert.That(actual, Does.Not.Match(expected)); | ||
} | ||
``` | ||
|
||
<!-- start generated config severity --> | ||
## Configure severity | ||
|
||
### Via ruleset file | ||
|
||
Configure the severity per project, for more info see [MSDN](https://learn.microsoft.com/en-us/visualstudio/code-quality/using-rule-sets-to-group-code-analysis-rules?view=vs-2022). | ||
|
||
### Via .editorconfig file | ||
|
||
```ini | ||
# NUnit2048: Consider using Assert.That(...) instead of StringAssert(...) | ||
dotnet_diagnostic.NUnit2048.severity = chosenSeverity | ||
``` | ||
|
||
where `chosenSeverity` can be one of `none`, `silent`, `suggestion`, `warning`, or `error`. | ||
|
||
### Via #pragma directive | ||
|
||
```csharp | ||
#pragma warning disable NUnit2048 // Consider using Assert.That(...) instead of StringAssert(...) | ||
Code violating the rule here | ||
#pragma warning restore NUnit2048 // Consider using Assert.That(...) instead of StringAssert(...) | ||
``` | ||
|
||
Or put this at the top of the file to disable all instances. | ||
|
||
```csharp | ||
#pragma warning disable NUnit2048 // Consider using Assert.That(...) instead of StringAssert(...) | ||
``` | ||
|
||
### Via attribute `[SuppressMessage]` | ||
|
||
```csharp | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Assertion", | ||
"NUnit2048:Consider using Assert.That(...) instead of StringAssert(...)", | ||
Justification = "Reason...")] | ||
``` | ||
<!-- end generated config severity --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageAnalyzerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using System.Collections.Generic; | ||
using Gu.Roslyn.Asserts; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using NUnit.Analyzers.Constants; | ||
using NUnit.Analyzers.StringAssertUsage; | ||
using NUnit.Framework; | ||
|
||
namespace NUnit.Analyzers.Tests.StringAssertUsage | ||
{ | ||
[TestFixture] | ||
internal class StringAssertUsageAnalyzerTests | ||
{ | ||
private readonly DiagnosticAnalyzer analyzer = new StringAssertUsageAnalyzer(); | ||
private readonly ExpectedDiagnostic diagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.StringAssertUsage); | ||
|
||
private static IEnumerable<string> StringAsserts => StringAssertUsageAnalyzer.StringAssertToConstraint.Keys; | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenNoArgumentsAreUsed(string method) | ||
{ | ||
var testCode = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual""); | ||
"); | ||
RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); | ||
} | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenOnlyMessageArgumentIsUsed(string method) | ||
{ | ||
var testCode = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual"", ""message""); | ||
"); | ||
RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); | ||
} | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenFormatAndArgumentsAreUsed(string method) | ||
{ | ||
var testCode = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual"", ""Because of {{0}}"", ""message""); | ||
"); | ||
RoslynAssert.Diagnostics(this.analyzer, this.diagnostic, testCode); | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
src/nunit.analyzers.tests/StringAssertUsage/StringAssertUsageCodeFixTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System.Collections.Generic; | ||
using Gu.Roslyn.Asserts; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using NUnit.Analyzers.Constants; | ||
using NUnit.Analyzers.StringAssertUsage; | ||
using NUnit.Framework; | ||
|
||
namespace NUnit.Analyzers.Tests.StringAssertUsage | ||
{ | ||
[TestFixture] | ||
internal sealed class StringAssertUsageCodeFixTests | ||
{ | ||
private static readonly DiagnosticAnalyzer analyzer = new StringAssertUsageAnalyzer(); | ||
private static readonly CodeFixProvider fix = new StringAssertUsageCodeFix(); | ||
private static readonly ExpectedDiagnostic expectedDiagnostic = ExpectedDiagnostic.Create(AnalyzerIdentifiers.StringAssertUsage); | ||
|
||
private static IEnumerable<string> StringAsserts => StringAssertUsageAnalyzer.StringAssertToConstraint.Keys; | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenNoArgumentsAreUsed(string method) | ||
{ | ||
var code = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual""); | ||
"); | ||
var fixedCode = TestUtility.WrapInTestMethod(@$" | ||
Assert.That(""actual"", {GetAdjustedConstraint(method)}); | ||
"); | ||
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); | ||
} | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenOnlyMessageArgumentIsUsed(string method) | ||
{ | ||
var code = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual"", ""message""); | ||
"); | ||
var fixedCode = TestUtility.WrapInTestMethod(@$" | ||
Assert.That(""actual"", {GetAdjustedConstraint(method)}, ""message""); | ||
"); | ||
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); | ||
} | ||
|
||
[TestCaseSource(nameof(StringAsserts))] | ||
public void AnalyzeWhenFormatAndArgumentsAreUsed(string method) | ||
{ | ||
var code = TestUtility.WrapInTestMethod(@$" | ||
↓StringAssert.{method}(""expected"", ""actual"", ""Because of {{0}}"", ""message""); | ||
"); | ||
var fixedCode = TestUtility.WrapInTestMethod(@$" | ||
Assert.That(""actual"", {GetAdjustedConstraint(method)}, $""Because of {{""message""}}""); | ||
"); | ||
RoslynAssert.CodeFix(analyzer, fix, expectedDiagnostic, code, fixedCode); | ||
} | ||
|
||
private static string GetAdjustedConstraint(string method) | ||
{ | ||
return StringAssertUsageAnalyzer.StringAssertToConstraint[method] | ||
.Replace("expected", "\"expected\""); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.