-
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 PosInfoMoq2007 rule to check the As<T>() use interface.
- Loading branch information
1 parent
f19dd54
commit 8f8d95d
Showing
10 changed files
with
317 additions
and
2 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
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 @@ | ||
# PosInfoMoq2007: The `As<T>()` method can be used only with interfaces. | ||
|
||
| Property | Value | | ||
|-------------------------------------|-----------------------------------------------------------------| | ||
| **Rule ID** | PosInfoMoq2007 | | ||
| **Title** | The `As<T>()` method can be used only with interfaces. | | ||
| **Category** | Compilation | | ||
| **Default severity** | Error | | ||
|
||
## Cause | ||
|
||
The `As<T>()` method is used with a type which is not an interface. | ||
|
||
## Rule description | ||
|
||
Moq allows to add additional implementations for mocked class (or interface) by adding additional interfaces | ||
with the `As<T>()` method. | ||
|
||
```csharp | ||
[Fact] | ||
public void Test() | ||
{ | ||
var service = new Mock<Service>(); | ||
service.As<IDisposable>() // Add IDisposable implementation for the mocked Service class. | ||
.Setup(s => s.Dispose()); | ||
service.As<OtherService>(); // An error will be raised, because we can't mock additional implementation of a class. | ||
} | ||
|
||
public abstract class Service | ||
{ | ||
} | ||
|
||
public abstract class OtherService | ||
{ | ||
} | ||
``` | ||
|
||
## How to fix violations | ||
|
||
To fix a violation of this rule, use an interface when using the `As<T>()` 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 *"Can only add interfaces to the mock."* 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
66 changes: 66 additions & 0 deletions
66
src/Moq.Analyzers/Analyzers/AsMustBeUsedWithInterfaceAnalyzer.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,66 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="AsMustBeUsedWithInterfaceAnalyzer.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 AsMustBeUsedWithInterfaceAnalyzer : DiagnosticAnalyzer | ||
{ | ||
internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( | ||
"PosInfoMoq2007", | ||
"The As<T>() method can be used only with interfaces", | ||
"The As<T>() method can be used only with interfaces", | ||
"Compilation", | ||
DiagnosticSeverity.Error, | ||
isEnabledByDefault: true, | ||
description: "The As<T>() method can be used only with interfaces."); | ||
|
||
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); | ||
|
||
var asMethodType = moqExpressionAnalyzer.ExtractAsMethodType(moqSymbols, invocationExpression, out var typeSyntax, context.CancellationToken); | ||
|
||
if (asMethodType is null) | ||
{ | ||
return; | ||
} | ||
|
||
if (asMethodType.TypeKind == TypeKind.Interface) | ||
{ | ||
return; | ||
} | ||
|
||
var diagnostic = Diagnostic.Create(Rule, typeSyntax!.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
146 changes: 146 additions & 0 deletions
146
tests/Moq.Analyzers.Tests/Analyzers/AsMustBeUsedWithInterfaceAnalyzerTest.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,146 @@ | ||
//----------------------------------------------------------------------- | ||
// <copyright file="AsMustBeUsedWithInterfaceAnalyzerTest.cs" company="P.O.S Informatique"> | ||
// Copyright (c) P.O.S Informatique. All rights reserved. | ||
// </copyright> | ||
//----------------------------------------------------------------------- | ||
|
||
namespace PosInformatique.Moq.Analyzers.Tests | ||
{ | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier< | ||
AsMustBeUsedWithInterfaceAnalyzer, | ||
Microsoft.CodeAnalysis.Testing.DefaultVerifier>; | ||
|
||
public class AsMustBeUsedWithInterfaceAnalyzerTest | ||
{ | ||
[Fact] | ||
public async Task AsMethod_WithInterface() | ||
{ | ||
var source = @" | ||
namespace ConsoleApplication1 | ||
{ | ||
using Moq; | ||
public class TestClass | ||
{ | ||
public void TestMethod() | ||
{ | ||
var mock1 = new Mock<C>(); | ||
mock1.As<I>(); | ||
} | ||
} | ||
public class C | ||
{ | ||
} | ||
public interface I | ||
{ | ||
} | ||
}" + MoqLibrary.Code; | ||
|
||
await Verify.VerifyAnalyzerAsync(source); | ||
} | ||
|
||
[Fact] | ||
public async Task AsMethod_WithClass() | ||
{ | ||
var source = @" | ||
namespace ConsoleApplication1 | ||
{ | ||
using Moq; | ||
public class TestClass | ||
{ | ||
public void TestMethod() | ||
{ | ||
var mock1 = new Mock<C>(); | ||
mock1.As<[|C2|]>(); | ||
} | ||
} | ||
public class C | ||
{ | ||
} | ||
public class C2 | ||
{ | ||
} | ||
}" + MoqLibrary.Code; | ||
|
||
await Verify.VerifyAnalyzerAsync(source); | ||
} | ||
|
||
[Fact] | ||
public async Task NoAsMethod() | ||
{ | ||
var source = @" | ||
namespace ConsoleApplication1 | ||
{ | ||
using Moq; | ||
public class TestClass | ||
{ | ||
public void TestMethod() | ||
{ | ||
var mock1 = new Mock<C>(); | ||
mock1.Setup(m => m.Method()); | ||
var action = new System.Action(() => { }); | ||
action(); // Ignored by the ExtractAsMethodType() method because not a MemberAccessExpressionSyntax. | ||
var otherClass = new OtherClass(); | ||
otherClass.GenericMethodNotAs<C>(); // Ignored by the ExtractAsMethodType() method because it not As<T>() symbol. | ||
} | ||
} | ||
public class C | ||
{ | ||
public virtual void Method() { } | ||
} | ||
public class OtherClass | ||
{ | ||
public void GenericMethodNotAs<T>() { } | ||
} | ||
}" + MoqLibrary.Code; | ||
|
||
await Verify.VerifyAnalyzerAsync(source); | ||
} | ||
|
||
[Fact] | ||
public async Task NoMoqLibrary() | ||
{ | ||
var source = @" | ||
namespace ConsoleApplication1 | ||
{ | ||
using OtherNamespace; | ||
public class TestClass | ||
{ | ||
public void TestMethod() | ||
{ | ||
var mock1 = new Mock<I>(); | ||
mock1.As<I>(); | ||
} | ||
} | ||
public interface I | ||
{ | ||
} | ||
} | ||
namespace OtherNamespace | ||
{ | ||
public class Mock<T> | ||
{ | ||
public void As<TInterface>() { } | ||
} | ||
}"; | ||
|
||
await Verify.VerifyAnalyzerAsync(source); | ||
} | ||
} | ||
} |
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