Skip to content

Commit

Permalink
Setups are considered harmful
Browse files Browse the repository at this point in the history
  • Loading branch information
fakefeik committed Aug 21, 2024
1 parent 8f0980d commit 0588763
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 26 deletions.
1 change: 1 addition & 0 deletions NUnit.Analyzers/Constants/AnalyzerIdentifiers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
internal static class AnalyzerIdentifiers
{
internal const string TestFieldIsNotReadonly = "NU0001";
internal const string TestUsesSetupAttributes = "NU0002";
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Immutable;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -11,9 +13,12 @@

namespace NUnit.Analyzers.TestFieldIsNotReadonly
{
[Shared]
[ExportCodeFixProvider(LanguageNames.CSharp)]
public class TestFieldIsNotReadonlyCodeFix : CodeFixProvider
{
private const string makeTestFieldReadonly = "Make test field readonly";

public override sealed FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
Expand All @@ -37,7 +42,22 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (originalExpression.Modifiers.Any(IsReadonlyModifier))
return;

// var newExpression = originalExpression.WithModifiers(originalExpression.Modifiers.Add(Synra))
var readonlySyntax = SyntaxFactory.Token(
SyntaxTriviaList.Empty,
SyntaxKind.ReadOnlyKeyword,
SyntaxTriviaList.Create(SyntaxFactory.Whitespace(" ")));

var addedReadonlyModifier = originalExpression.Modifiers.Add(readonlySyntax);
var newExpression = originalExpression.WithModifiers(addedReadonlyModifier);

var newRoot = root.ReplaceNode(originalExpression, newExpression);

var codeAction = CodeAction.Create(
makeTestFieldReadonly,
_ => Task.FromResult(context.Document.WithSyntaxRoot(newRoot)),
makeTestFieldReadonly);

context.RegisterCodeFix(codeAction, context.Diagnostics);
}

private static bool IsReadonlyModifier(SyntaxToken syntaxToken) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace NUnit.Analyzers.TestFieldIsNotReadonly
namespace NUnit.Analyzers.TestFieldIsNotReadonly
{
internal class TestFieldIsNotReadonlyConstants
internal static class TestFieldIsNotReadonlyConstants
{
internal const string TestFieldIsNotReadonlyTitle = "The field in test class is not readonly";
internal const string TestFieldIsNotReadonlyMessage = "Fields in test classes should be readonly";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Immutable;
using System.Linq;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

using NUnit.Analyzers.Constants;
using NUnit.Analyzers.Extensions;

namespace NUnit.Analyzers.TestUsesSetupMethods
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class TestUsesSetupMethodsAnalyzer : DiagnosticAnalyzer
{
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
}

private static void AnalyzeMethod(SymbolAnalysisContext context)
{
var methodSymbol = (IMethodSymbol)context.Symbol;
if (IsSetUpTearDownMethod(context.Compilation, methodSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(testUsesSetupMethods, methodSymbol.Locations[0]));
}
}

private static bool IsSetUpTearDownMethod(Compilation compilation, IMethodSymbol methodSymbol)
{
return methodSymbol.GetAttributes().Any(a => a.IsSetUpOrTearDownMethodAttribute(compilation));
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(testUsesSetupMethods);

private static readonly DiagnosticDescriptor testUsesSetupMethods = new DiagnosticDescriptor(
AnalyzerIdentifiers.TestUsesSetupAttributes,

Check warning on line 40 in NUnit.Analyzers/TestUsesSetupMethods/TestUsesSetupMethodsAnalyzer.cs

View workflow job for this annotation

GitHub Actions / test

Enable analyzer release tracking for the analyzer project containing rule 'NU0002' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 40 in NUnit.Analyzers/TestUsesSetupMethods/TestUsesSetupMethodsAnalyzer.cs

View workflow job for this annotation

GitHub Actions / test

Enable analyzer release tracking for the analyzer project containing rule 'NU0002' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 40 in NUnit.Analyzers/TestUsesSetupMethods/TestUsesSetupMethodsAnalyzer.cs

View workflow job for this annotation

GitHub Actions / test

Enable analyzer release tracking for the analyzer project containing rule 'NU0002' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 40 in NUnit.Analyzers/TestUsesSetupMethods/TestUsesSetupMethodsAnalyzer.cs

View workflow job for this annotation

GitHub Actions / test

Enable analyzer release tracking for the analyzer project containing rule 'NU0002' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)
TestUsesSetupMethodsConstants.TestUsesSetupMethodsTitle,
TestUsesSetupMethodsConstants.TestUsesSetupMethodsMessage,
Categories.ParallelExecution,
DiagnosticSeverity.Warning,
true,
TestUsesSetupMethodsConstants.TestUsesSetupMethodsDescription,
TestUsesSetupMethodsConstants.TestUsesSetupMethodsUri
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace NUnit.Analyzers.TestUsesSetupMethods
{
internal class TestUsesSetupMethodsConstants
{
internal const string TestUsesSetupMethodsTitle = "Test uses setup methods";
internal const string TestUsesSetupMethodsMessage = "Setup methods are considered harmful";
internal const string TestUsesSetupMethodsDescription = "If you require a similar object or state for your tests, prefer a helper method than using Setup and Teardown attributes.";
internal const string TestUsesSetupMethodsUri = "https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices#prefer-helper-methods-to-setup-and-teardown";
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Linq;
using System.Linq;

using FluentAssertions;

using NUnit.Framework;
using NUnit.Framework.Internal.Execution;

using SkbKontur.NUnit.Middlewares;

Expand All @@ -24,21 +22,13 @@ namespace SkbKontur.NUnit.Extensions.Tests.Middlewares
[Parallelizable(ParallelScope.Self)]
public class ParallelParametrizedTestFixtureTest : SimpleTestBase
{
private event Action E;
private readonly int i1;
private int i2;
private const int i3 = 0;

public ParallelParametrizedTestFixtureTest(int i, int i2)
{
this.i = i;
this.i1 = 1;
E += () => {};
}

protected override void Configure(ISetupBuilder fixture, ISetupBuilder test)
{
i2 = 3;
fixture
.UseSetup<TestInvocationCounterSetup>()
.Use(t => t.GetFromThisOrParentContext<Counter>().InvocationsCount += i);
Expand All @@ -47,10 +37,6 @@ protected override void Configure(ISetupBuilder fixture, ISetupBuilder test)
[Test]
public void Test()
{
E();
Console.WriteLine(i1);
Console.WriteLine(i2);
Console.WriteLine(i3);
var counter = SimpleTestContext.Current.Get<Counter>();
counter.Should().NotBeNull();

Expand Down
12 changes: 5 additions & 7 deletions NUnit.Extensions.Tests/NUnit.Extensions.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="FluentAssertions.Analyzers" Version="0.33.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.3.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NUnit.Analyzers\NUnit.Analyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\NUnit.Middlewares\NUnit.Middlewares.csproj" />
Expand Down

0 comments on commit 0588763

Please sign in to comment.