-
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 the rule PosInfoMoq2006 to check Protected() method setup.
- Loading branch information
1 parent
7ac080f
commit 4f28a5e
Showing
10 changed files
with
532 additions
and
3 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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# PosInfoMoq2006: The Protected().Setup() method must be use with overridable protected or internal methods | ||
|
||
| Property | Value | | ||
|-------------------------------------|----------------------------------------------------------------------------------------------| | ||
| **Rule ID** | PosInfoMoq2006 | | ||
| **Title** | The `Protected().Setup()` method must be use with overridable protected or internal methods. | | ||
| **Category** | Compilation | | ||
| **Default severity** | Error | | ||
|
||
## Cause | ||
|
||
A `Protected().Setup()` reference a method in the mocked type which have the following criteria: | ||
- Is not existing | ||
- Is not virtual | ||
- Is not abstract | ||
|
||
## Rule description | ||
|
||
When using the `Protected().Setup()`, the method mocked must be `protected`, `internal` or `protected internal`, | ||
and must be overridable (`virtual` or `abstract`). | ||
|
||
```csharp | ||
[Fact] | ||
public void Test() | ||
{ | ||
var service = new Mock<Service>(1, 2, 3); | ||
service.Protected().Setup("GetData") // The GetData() is public and can be mocked with Protected() feature. | ||
.Returns(10); | ||
service.Protected().Setup("NotExists") // The NotExists() method does not exist. | ||
.Returns(10); | ||
service.Protected().Setup("YouCantOverrideMe") // The YouCantOverrideMe() is not virtual or abstract. | ||
.Returns(10); | ||
} | ||
|
||
public abstract class Service | ||
{ | ||
public abstract int GetData(); | ||
|
||
protected void YouCantOverrideMe() { }; | ||
} | ||
``` | ||
|
||
## How to fix violations | ||
|
||
To fix a violation of this rule, use the `Protected().Setup()` to mock method which are: | ||
- `protected` | ||
- `internal` | ||
- `protected internal` | ||
- Overridable (`virtual` or `abstract`). | ||
|
||
Else use the standard mocking feature without the `Protected()` method. | ||
|
||
## When to suppress warnings | ||
|
||
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `MoqException` | ||
thrown with the *"Method X.xxxx is public. Use strong-typed."* message. |
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
111 changes: 111 additions & 0 deletions
111
...Moq.Analyzers/Analyzers/SetupProtectedMustBeUsedWithProtectedOrInternalMembersAnalyzer.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,111 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="SetupProtectedMustBeUsedWithProtectedOrInternalMembersAnalyzer.cs" company="P.O.S Informatique"> | ||
// Copyright (c) P.O.S Informatique. All rights reserved. | ||
// </copyright> | ||
//----------------------------------------------------------------------- | ||
|
||
namespace PosInformatique.Moq.Analyzers | ||
{ | ||
using System.Collections.Immutable; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
public class SetupProtectedMustBeUsedWithProtectedOrInternalMembersAnalyzer : DiagnosticAnalyzer | ||
{ | ||
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( | ||
"PosInfoMoq2006", | ||
"The Protected().Setup() method must be use with overridable protected or internal methods", | ||
"The Protected().Setup() method must be use with overridable protected or internal methods", | ||
"Compilation", | ||
DiagnosticSeverity.Error, | ||
isEnabledByDefault: true, | ||
description: "The Protected().Setup() method must be use with overridable protected or internal methods."); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
context.EnableConcurrentExecution(); | ||
|
||
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.InvocationExpression); | ||
} | ||
|
||
private static void Analyze(SyntaxNodeAnalysisContext context) | ||
{ | ||
var invocationExpression = (InvocationExpressionSyntax)context.Node; | ||
|
||
var moqSymbols = MoqSymbols.FromCompilation(context.Compilation); | ||
|
||
if (moqSymbols is null) | ||
{ | ||
return; | ||
} | ||
|
||
var moqExpressionAnalyzer = new MoqExpressionAnalyzer(context.SemanticModel); | ||
|
||
// Check is Protected() method. | ||
if (!moqExpressionAnalyzer.IsMockSetupMethodProtected(moqSymbols, invocationExpression, out var localVariableExpression, context.CancellationToken)) | ||
{ | ||
return; | ||
} | ||
|
||
// Gets the first argument to retrieve the name of the method. | ||
if (invocationExpression.ArgumentList is null) | ||
{ | ||
return; | ||
} | ||
|
||
if (invocationExpression.ArgumentList.Arguments.Count == 0) | ||
{ | ||
return; | ||
} | ||
|
||
if (invocationExpression.ArgumentList.Arguments[0].Expression is not LiteralExpressionSyntax literalExpression) | ||
{ | ||
return; | ||
} | ||
|
||
var methodName = literalExpression.Token.ValueText; | ||
|
||
// Gets the mocked type | ||
var mockedType = moqExpressionAnalyzer.GetMockedType(moqSymbols, localVariableExpression!, context.CancellationToken); | ||
|
||
if (mockedType is null) | ||
{ | ||
return; | ||
} | ||
|
||
// Check if a method exists with the specified name | ||
foreach (var method in mockedType.GetMembers(methodName).OfType<IMethodSymbol>()) | ||
{ | ||
if (!method.IsAbstract && !method.IsVirtual) | ||
{ | ||
continue; | ||
} | ||
|
||
if (method.DeclaredAccessibility == Accessibility.Protected) | ||
{ | ||
return; | ||
} | ||
|
||
if (method.DeclaredAccessibility == Accessibility.Internal) | ||
{ | ||
return; | ||
} | ||
|
||
if (method.DeclaredAccessibility == Accessibility.ProtectedOrInternal) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
// No returns method has been specified with Strict mode. Report the diagnostic issue. | ||
var diagnostic = Diagnostic.Create(Rule, literalExpression.GetLocation()); | ||
context.ReportDiagnostic(diagnostic); | ||
} | ||
} | ||
} |
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.