Skip to content

Commit

Permalink
feat: add suppressor of CA1707 for test methods (#97)
Browse files Browse the repository at this point in the history
enable .NET analyzers
  • Loading branch information
Flash0ver authored Mar 20, 2022
1 parent 03f07c4 commit 66a4543
Show file tree
Hide file tree
Showing 57 changed files with 1,856 additions and 109 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
CHANGELOG

## vNext
### Analyzers
- Added `IdentifiersShouldNotContainUnderscores` diagnostic suppressor, suppressing _Warning CA1707_ for _MSTest_, _NUnit_ and _xUnit.net_ test methods.

## v0.9.0 (2022-02-04)
### Analyzers
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ requires _Roslyn v4.0.1_ or later (see [packages of the .NET Compiler Platform](
### .NET 6.0
supports [SDK 6.0.100](https://github.com/dotnet/core/blob/main/release-notes/6.0/6.0.0/6.0.0.md) and later
### Visual Studio 2022
supports [17.0.0](https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes#17.0.0) and later
supports [17.0.0](https://docs.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.0) and later
### Rider
supports [2021.3](https://www.jetbrains.com/rider/whatsnew/2021-3/) and later
### Visual Studio Code
supports _OmniSharp (C#)_ [1.24.0](https://github.com/OmniSharp/omnisharp-vscode/releases/tag/v1.24.0) and later
supports _OmniSharp (C#)_ [1.24.1](https://github.com/OmniSharp/omnisharp-vscode/releases/tag/v1.24.1) and later
7 changes: 7 additions & 0 deletions documentation/F0.Analyzers.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* [Diagnostic Analyzers](#diagnostic-analyzers)
* [Code Fixes](#code-fixes)
* [Code Refactorings](#code-refactorings)
* [Diagnostic Suppressors](#diagnostic-suppressors)

## Diagnostic Analyzers

Expand All @@ -27,3 +28,9 @@ Namespace: [F0.CodeAnalysis.CodeFixes](../source/production/F0.Analyzers/CodeAna
Namespace: [F0.CodeAnalysis.CodeRefactorings](../source/production/F0.Analyzers/CodeAnalysis/CodeRefactorings/)

* [ObjectInitializer](./refactorings/ObjectInitializer.md)

## Diagnostic Suppressors

Namespace: [F0.CodeAnalysis.Suppressors](../source/production/F0.Analyzers/CodeAnalysis/Suppressors/)

* CA1707 [IdentifiersShouldNotContainUnderscoresSuppressor](./suppressors/CA1707IdentifiersShouldNotContainUnderscoresSuppressor.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Suppress CA1707 for test methods

DiagnosticSuppressor: [CA1707IdentifiersShouldNotContainUnderscoresSuppressor.cs](../../source/production/F0.Analyzers/CodeAnalysis/Suppressors/CA1707IdentifiersShouldNotContainUnderscoresSuppressor.cs)

| | |
|------------|------------------|
| ID | F0CA1707 |
| Suppresses | [CA1707][ca1707] |
| Applies to | `[vNext,)` |

## Summary

Suppress warning [CA1707][ca1707] on test methods to allow the best practices naming standard for tests.

## Remarks

[CA1707][ca1707] reports a warning on non-field identifiers when containing underscores, i.e., a `_` character.

The [best practices for writing unit tests in .NET](https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices) suggest the following [naming standard](https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices#naming-your-tests):

> The name of your test should consist of three parts:
>
> - The name of the method being tested.
> - The scenario under which it's being tested.
> - The expected behavior when the scenario is invoked.
These parts should be separated by the _underscore_ (`_`) character.

[CA1707][ca1707] is suppressed only for _MSTest_, _NUnit_ and _xUnit.net_ test methods.
For regular methods and other identifiers, [CA1707][ca1707] is not suppressed.

### MSTest
A method is a _MSTest_ test method, when it has
- either the `TestMethodAttribute`
- or the `DataTestMethodAttribute`

and the containing _class_ is attributed with
- `TestClassAttribute`

from the _namespace_ `Microsoft.VisualStudio.TestTools.UnitTesting`.

### NUnit
A method is an _NUnit_ test method, when it has one of the following _attributes_ (Namespace: `NUnit.Framework`):
- `TestAttribute`
- `TestCaseAttribute`
- `TestCaseSourceAttribute`
- `CombinatorialAttribute`
- `PairwiseAttribute`
- `SequentialAttribute`
- `TheoryAttribute`

### xUnit.net
A method is a _xUnit.net_ test method, when it has one of the following _attributes_ (Namespace: `Xunit`):
- `FactAttribute`
- `TheoryAttribute`

## Example

```cs
using Xunit;

namespace My_Namespace; // Remove the underscores from namespace name 'My_Namespace'
public class My_Tests // Remove the underscores from type name My_Namespace.My_Tests
{
[Fact]
public void Given_When_Then() // Warning CA1707 suppressed
{
Assert.Equal(240, 0x_F0);
}

[Theory]
[InlineData(0x_F0)]
public void MethodUnderTest_Scenario_ExpectedResult(int value) // Warning CA1707 suppressed
{
Assert.Equal(240, value);
}

public int My_Property { get; set; } // Remove the underscores from member name My_Namespace.My_Tests.My_Property
public void My_Method() // Remove the underscores from member name My_Namespace.My_Tests.My_Method()
=> throw null;

public void MyMethod<Method_TypeParameter>() // On method My_Namespace.My_Tests.MyMethod<Method_TypeParameter>(), remove the underscores from generic type parameter name Method_TypeParameter
=> throw null;
}
```

## See also

- [Warning CA1707][ca1707]
- [MSTest](https://github.com/microsoft/testfx)
- [NUnit](https://github.com/nunit/nunit)
- [xUnit.net](https://github.com/xunit/xunit)

## History

- [vNext](../../CHANGELOG.md#vNext)


[ca1707]: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1707
1 change: 1 addition & 0 deletions nuget.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
</packageSources>
</configuration>
17 changes: 17 additions & 0 deletions source/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>6.0</AnalysisLevel>
<AnalysisMode>All</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<PropertyGroup>
<NoWarn />
<WarningsAsErrors />
Expand All @@ -14,10 +21,20 @@

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<AssemblyAttribute Include="System.CLSCompliantAttribute">
<_Parameter1>false</_Parameter1>
<_Parameter1_TypeName>System.Boolean</_Parameter1_TypeName>
<_Parameter1_IsLiteral>false</_Parameter1_IsLiteral>
</AssemblyAttribute>
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions source/F0.Analyzers.Core.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"example\\F0.Analyzers.Example.CSharp8\\F0.Analyzers.Example.CSharp8.csproj",
"example\\F0.Analyzers.Example.CSharp9\\F0.Analyzers.Example.CSharp9.csproj",
"example\\F0.Analyzers.Example.Dependencies\\F0.Analyzers.Example.Dependencies.csproj",
"example\\F0.Analyzers.Example.Tests\\F0.Analyzers.Example.Tests.csproj",
"example\\F0.Analyzers.Example\\F0.Analyzers.Example.csproj",
"package\\F0.Analyzers.Package\\F0.Analyzers.Package.csproj",
"performance\\F0.Analyzers.Benchmarks\\F0.Analyzers.Benchmarks.csproj",
Expand Down
1 change: 1 addition & 0 deletions source/F0.Analyzers.Example.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"example\\F0.Analyzers.Example.CSharp8\\F0.Analyzers.Example.CSharp8.csproj",
"example\\F0.Analyzers.Example.CSharp9\\F0.Analyzers.Example.CSharp9.csproj",
"example\\F0.Analyzers.Example.Dependencies\\F0.Analyzers.Example.Dependencies.csproj",
"example\\F0.Analyzers.Example.Tests\\F0.Analyzers.Example.Tests.csproj",
"example\\F0.Analyzers.Example\\F0.Analyzers.Example.csproj"
]
}
Expand Down
9 changes: 8 additions & 1 deletion source/F0.Analyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "F0.Analyzers.Example.CSharp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "F0.Analyzers.Example.CSharp9", "example\F0.Analyzers.Example.CSharp9\F0.Analyzers.Example.CSharp9.csproj", "{BF6085B7-EF86-49A1-9FED-9308982A7A89}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F0.Analyzers.Example.CSharp10", "example\F0.Analyzers.Example.CSharp10\F0.Analyzers.Example.CSharp10.csproj", "{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "F0.Analyzers.Example.CSharp10", "example\F0.Analyzers.Example.CSharp10\F0.Analyzers.Example.CSharp10.csproj", "{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F0.Analyzers.Example.Tests", "example\F0.Analyzers.Example.Tests\F0.Analyzers.Example.Tests.csproj", "{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -105,6 +107,10 @@ Global
{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4}.Release|Any CPU.Build.0 = Release|Any CPU
{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -124,6 +130,7 @@ Global
{0C22B8EE-947D-4962-BBB3-DC1381E96436} = {EF42DC6B-A41F-4459-83E0-296E02BD86B9}
{BF6085B7-EF86-49A1-9FED-9308982A7A89} = {EF42DC6B-A41F-4459-83E0-296E02BD86B9}
{4FF74BB6-FD3D-443A-99F7-A5113EBE56C4} = {EF42DC6B-A41F-4459-83E0-296E02BD86B9}
{A8481C5B-7F58-4198-80AF-ECBE18BB9EC7} = {EF42DC6B-A41F-4459-83E0-296E02BD86B9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4C250660-2192-45B9-A87B-5E23C0AAA835}
Expand Down
4 changes: 4 additions & 0 deletions source/example/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>

<PropertyGroup>
<AnalysisMode>Default</AnalysisMode>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
using System.Reflection;

[assembly: AssemblyDescription("F0.Analyzers code sample.")]
[assembly: AssemblyCopyright("Copyright © f[0] 2022")]
4 changes: 4 additions & 0 deletions source/example/F0.Analyzers.Example.Tests/.globalconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
is_global = true

# CA1707: Identifiers should not contain underscores
dotnet_diagnostic.CA1707.severity = none
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using F0.Analyzers.Example.Tests.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace F0.Analyzers.Example.Tests.CodeAnalysis.Suppressors
{
[TestClass]
public class MSTest
{
private readonly PrimeService primeService;

public MSTest()
{
primeService = new PrimeService();
}

[TestMethod]
public void IsPrime_InputIs1_ReturnFalse()
{
var result = primeService.IsPrime(1);

Assert.IsFalse(result, "1 should not be prime");
}

[DataTestMethod]
[DataRow(-1)]
[DataRow(0)]
[DataRow(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = primeService.IsPrime(value);

Assert.IsFalse(result, $"{value} should not be prime");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Diagnostics.CodeAnalysis;
using F0.Analyzers.Example.Tests.Services;
using NUnit.Framework;

namespace F0.Analyzers.Example.Tests.CodeAnalysis.Suppressors
{
[TestFixture]
[SuppressMessage("Style", "IDE0022:Use expression body for methods", Justification = "Example")]
public class NUnit
{
private PrimeService primeService;

[SetUp]
public void SetUp()
{
primeService = new PrimeService();
}

[Test]
public void IsPrime_InputIs1_ReturnFalse()
{
var result = primeService.IsPrime(1);

Assert.IsFalse(result, "1 should not be prime");
}

[TestCase(-1)]
[TestCase(0)]
[TestCase(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = primeService.IsPrime(value);

Assert.IsFalse(result, $"{value} should not be prime");
}

[TestCaseSource(nameof(TestCaseSource))]
public void TestCaseSource_Attribute(int value)
{
var result = primeService.IsPrime(value);

Assert.IsFalse(result, $"{value} should not be prime");
}

private static int[] TestCaseSource => new int[] { -2, -3 };

[Combinatorial]
public void Combinatorial_Attribute([Values(1, 2)] int number, [Values("A", "B")] string text)
{
var actual = number + text;

Assert.That(actual, Is.AnyOf("1A", "1B", "2A", "2B"));
}

[Pairwise]
public void Pairwise_Attribute([Values("a", "b", "c")] string left, [Values("+", "-")] string middle, [Values("x", "y")] string right)
{
var actual = left + middle + right;

Assert.That(actual, Is.AnyOf("a-x", "a+y", "b+x", "b-y", "c-x", "c+y"));
}

[Sequential]
public void Sequential_Attribute([Values(1, 2, 3)] int number, [Values("A", "B")] string text)
{
var actual = number + text;

Assert.That(actual, Is.AnyOf("1A", "2B", "3"));
}

[Theory]
public void Theory_Attribute(int number)
{
Assume.That(number != 0);

var @delegate = () => 0 / number;

Assert.That(@delegate, Throws.Nothing);
}

[DatapointSource]
public int[] DatapointSource => new int[] { -1, 0, 1 };
}
}
Loading

0 comments on commit 66a4543

Please sign in to comment.