-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Clean up README and add docs for each analyzer rule (#77)
Made a pass at the README and other docs to make the project easier to use. - Moved the code of conduct out of README into `CODE-OF-CONDUCT.md` so that GitHub can pick it up automatically and add it to the UI - Move the contributing docs out of README and into `CONTRIBUTING.md` so that GitHub can pick it up automatically and add it to the UI - Add NuGet and build badges to README, for #15 - Small copy edits to README - Added a `//docs/rules/` folder and added a doc per rule with an explanation and sample
- Loading branch information
1 parent
9a3e801
commit 5a8cad8
Showing
12 changed files
with
339 additions
and
18 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,6 @@ | ||
# Code of Conduct | ||
|
||
This project has adopted the code of conduct defined by the Contributor Covenant | ||
to clarify expected behavior in our community. | ||
|
||
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct). |
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,7 @@ | ||
# Contributing | ||
|
||
We welcome contributions. If you want to contribute to existing issues, check the | ||
[help wanted](https://github.com/rjmurillo/moq.analyzers/labels/help%20wanted) or | ||
[good first issue](https://github.com/rjmurillo/moq.analyzers/labels/good%20first%20issue) items in the backlog. | ||
|
||
If you have new ideas or want to complain about bugs, feel free to [create a new issue](https://github.com/rjmurillo/moq.analyzers/issues/new). |
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 |
---|---|---|
@@ -1,30 +1,38 @@ | ||
# Moq.Analyzers | ||
|
||
**Moq.Analyzers** is a Roslyn analyzer that helps to write unit tests using the popular and friend [Moq](https://github.com/devlooped/moq) library. Moq.Analyzers protects you from popular mistakes and warns you if something is wrong with your Moq configuration: | ||
[![NuGet Version](https://img.shields.io/nuget/v/Moq.Analyzers?style=flat&logo=nuget&color=blue)](https://www.nuget.org/packages/Moq.Analyzers) | ||
[![NuGet Downloads](https://img.shields.io/nuget/dt/Moq.Analyzers?style=flat&logo=nuget)](https://www.nuget.org/packages/Moq.Analyzers) | ||
[![Main build](https://github.com/rjmurillo/moq.analyzers/actions/workflows/main.yml/badge.svg)](https://github.com/rjmurillo/moq.analyzers/actions/workflows/main.yml) | ||
|
||
## Detected issues | ||
**Moq.Analyzers** is a Roslyn analyzer that helps you to write unit tests using the popular | ||
[Moq](https://github.com/devlooped/moq) framework. Moq.Analyzers protects you from common mistakes and warns you if | ||
something is wrong with your Moq configuration. | ||
|
||
* Moq1000 = Sealed classes cannot be mocked. | ||
* Moq1001 = Mocked interfaces cannot have constructor parameters. | ||
* Moq1002 = Parameters provided into mock do not match any existing constructors. | ||
* Moq1100 = Callback signature must match the signature of the mocked method. | ||
* Moq1101 = SetupGet/SetupSet should be used for properties, not for methods. | ||
* Moq1200 = Setup should be used only for overridable members. | ||
* Moq1201 = Setup of async methods should use `.ReturnsAsync` instance instead of `.Result`. | ||
* Moq1300 = Mock.As() should take interfaces. | ||
## Analyzer rules | ||
|
||
## How to install | ||
* Moq1000: Sealed classes cannot be mocked | ||
* Moq1001: Mocked interfaces cannot have constructor parameters | ||
* Moq1002: Parameters provided into mock do not match any existing constructors | ||
* Moq1100: Callback signature must match the signature of the mocked method | ||
* Moq1101: SetupGet/SetupSet should be used for properties, not for methods | ||
* Moq1200: Setup should be used only for overridable members | ||
* Moq1201: Setup of async methods should use `.ReturnsAsync` instance instead of `.Result` | ||
* Moq1300: Mock.As() should take interfaces | ||
|
||
Install ["Moq.Analyzers" NuGet package](https://www.nuget.org/packages/Moq.Analyzers) into test projects using Moq. | ||
See [docs/rules](./docs/rules/README.md) for full documentation. | ||
|
||
You must use an in-support version of the .NET SDK (i.e. 6+). | ||
## Getting started | ||
|
||
## Contributions are welcome! | ||
Moq.Analyzers is installed from NuGet. Run this command for your test project(s): | ||
|
||
Moq.Analyzers continues to evolve and add new features. Any help will be appreciated. You can report issues, develop new features, improve the documention, or do other cool stuff. | ||
```powershell | ||
dotnet add package Moq.Analyzers | ||
``` | ||
|
||
If you want to contribute to existing issues, check the [help wanted](https://github.com/rjmurillo/moq.analyzers/labels/help%20wanted) or [good first issue](https://github.com/rjmurillo/moq.analyzers/labels/good%20first%20issue) items in the backlog. If you have new ideas or want to complain about bugs, feel free to [create a new issue](https://github.com/rjmurillo/moq.analyzers/issues/new). | ||
> NOTE: You must use a [supported version](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) of | ||
> the .NET SDK (i.e. 6.0 or later). | ||
## Code of Conduct | ||
## Contributions welcome | ||
|
||
This project has adopted the code of conduct defined by the [Contributor Covenant](https://www.contributor-covenant.org/) to set expectations for behavior in our communication. For more information, see the [.NET Foundation's Contributor Convenant Code of Conduct](https://dotnetfoundation.org/about/policies/code-of-conduct) | ||
Moq.Analyzers continues to evolve and add new features. Any help will be appreciated. You can report issues, | ||
develop new features, improve the documentation, or do other cool stuff. See [CONTRIBUTING.md](./CONTRIBUTING.md). |
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,30 @@ | ||
# Moq1000: Sealed classes cannot be mocked | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Warning | | ||
| CodeFix | False | | ||
--- | ||
|
||
Mocking requires generating a subclass of the class to be mocked. Sealed classes cannot be subclassed. To fix: | ||
|
||
- Introduce an interface and mock that instead | ||
- Use the real class and not a mock | ||
- Unseal the class | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
sealed class MyClass { } | ||
|
||
var mock = new Mock<MyClass>(); // Moq1000: Sealed classes cannot be mocked | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
class MyClass { } | ||
|
||
var mock = new Mock<MyClass>(); | ||
``` |
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,35 @@ | ||
# Moq1001: Mocked interfaces cannot have constructor parameters | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Warning | | ||
| CodeFix | False | | ||
--- | ||
|
||
Mocking interfaces requires generating a class on-the-fly that implements the interface. That generated class is | ||
constructed using the default constructor. To fix: | ||
|
||
- Remove the constructor parameters | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
interface IMyService | ||
{ | ||
void Do(string s); | ||
} | ||
|
||
var mock = new Mock<IMyService>("123"); // Moq1001: Mocked interfaces cannot have constructor parameters | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
interface IMyService | ||
{ | ||
void Do(string s); | ||
} | ||
|
||
var mock = new Mock<IMyService>(); | ||
``` |
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,34 @@ | ||
# Moq1002: Parameters provided into mock do not match any existing constructors | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Warning | | ||
| CodeFix | False | | ||
--- | ||
|
||
In order to construct the mocked type, constructor parameters must match a constructor. To fix: | ||
|
||
- Match the arguments to `Mock` with a constructor of the mocked type | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
class MyClass | ||
{ | ||
MyClass(string s) { } | ||
} | ||
|
||
var mock = new Mock<MyClass>(3); // Moq1002: Parameters provided into mock do not match any existing constructors | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
class MyClass | ||
{ | ||
MyClass(string s) { } | ||
} | ||
|
||
var mock = new Mock<MyClass>("three"); | ||
``` |
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,39 @@ | ||
# Moq1100: Callback signature must match the signature of the mocked method | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Warning | | ||
| CodeFix | True | | ||
--- | ||
|
||
The signature of the `.Callback()` method must match the signature of the `.Setup()` method. To fix: | ||
|
||
- Ensure the parameters to `.Callback()` match the signature created by `.Setup()`. A code fix is available to automatically | ||
match | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
interface IMyService | ||
{ | ||
int Do(int i, string s, DateTime dt); | ||
} | ||
|
||
var mock = new Mock<IMyService>() | ||
.Setup(x => x.Do(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<DateTime>())) | ||
.Callback((string s1, int i1) => { }); // Moq1100: Callback signature must match the signature of the mocked method | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
interface IMyService | ||
{ | ||
int Do(int i, string s, DateTime dt); | ||
} | ||
|
||
var mock = new Mock<IMyService>() | ||
.Setup(x => x.Do(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<DateTime>())) | ||
.Callback((int i, string s, DateTime dt) => { }); | ||
``` |
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,32 @@ | ||
# Moq1101: SetupGet/SetupSet should be used for properties, not for methods | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Warning | | ||
| CodeFix | False | | ||
--- | ||
|
||
`.SetupGet()` and `.SetupSet()` are methods for mocking properties, not methods. Use `.Setup()` to mock methods instead. | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
interface IMyInterface | ||
{ | ||
string Method(); | ||
} | ||
|
||
var mock = new Mock<IMyInterface>().SetupGet(x => x.Method()); // Moq1101: SetupGet/SetupSet should be used for properties, not for methods | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
interface IMyInterface | ||
{ | ||
string Method(); | ||
} | ||
|
||
var mock = new Mock<IMyInterface>().Setup(x => x.Method()); | ||
``` |
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,36 @@ | ||
# Moq1200: Setup should be used only for overridable members | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Error | | ||
| CodeFix | False | | ||
--- | ||
|
||
Mocking requires generating a subclass of the class to be mocked. Methods not marked `virtual` cannot be overridden. | ||
To fix: | ||
|
||
- Mock an interface instead of a clas | ||
- Make the method to be mocked `virtual` | ||
|
||
```csharp | ||
class SampleClass | ||
{ | ||
int Property { get; set; } | ||
} | ||
|
||
var mock = new Mock<SampleClass>() | ||
.Setup(x => x.Property); // Moq1200: Setup should be used only for overridable members | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
class SampleClass | ||
{ | ||
virtual int Property { get; set; } | ||
} | ||
|
||
var mock = new Mock<SampleClass>() | ||
.Setup(x => x.Property); | ||
``` |
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,35 @@ | ||
# Moq1201: Setup of async methods should use `.ReturnsAsync` instance instead of `.Result` | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Error | | ||
| CodeFix | False | | ||
--- | ||
|
||
Moq now supports the `.ReturnsAsync()` method to support mocking async methods. Use it instead of returning `.Result`, | ||
[which can cause issues](https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#avoid-using-taskresult-and-taskwait). | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
class AsyncClient | ||
{ | ||
virtual Task<string> GetAsync() => Task.FromResult(string.Empty); | ||
} | ||
|
||
var mock = new Mock<AsyncClient>() | ||
.Setup(c => c.GetAsync().Result); // Moq1201: Setup of async methods should use .ReturnsAsync instance instead of .Result | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
class AsyncClient | ||
{ | ||
virtual Task<string> GetAsync() => Task.FromResult(string.Empty); | ||
} | ||
|
||
var mock = new Mock<AsyncClient>() | ||
.Setup(c => c.GetAsync()).ReturnsAsync(string.Empty); | ||
``` |
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,47 @@ | ||
# Moq1300: `Mock.As()` should take interfaces only | ||
|
||
| Item | Value | | ||
| --- | --- | | ||
| Enabled | True | | ||
| Severity | Error | | ||
| CodeFix | False | | ||
--- | ||
|
||
The `.As()` method is used when a mocked object must implement multiple interfaces. It cannot be used with abstract or | ||
concrete classes. To fix: | ||
|
||
- Change the method to use an interface | ||
- Remove the `.As()` method | ||
|
||
## Examples of patterns that are flagged by this analyzer | ||
|
||
```csharp | ||
interface ISampleInterface | ||
{ | ||
int Calculate(int a, int b); | ||
} | ||
|
||
class SampleClass | ||
{ | ||
int Calculate() => 0; | ||
} | ||
|
||
var mock = new Mock<SampleClass>() | ||
.As<SampleClass>(); // Moq1300: Mock.As() should take interfaces only | ||
``` | ||
|
||
## Solution | ||
|
||
```csharp | ||
interface ISampleInterface | ||
{ | ||
int Calculate(int a, int b); | ||
} | ||
|
||
class SampleClass | ||
{ | ||
int Calculate() => 0; | ||
} | ||
|
||
var mock = new Mock<ISampleInterface>(); | ||
``` |
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,12 @@ | ||
# Diagnostics / rules | ||
|
||
| ID | Title | | ||
| --- | --- | | ||
[Moq1000](./Moq1000.md) | Sealed classes cannot be mocked | ||
[Moq1001](./Moq1001.md) | Mocked interfaces cannot have constructor parameters | ||
[Moq1002](./Moq1002.md) | Parameters provided into mock do not match any existing constructors | ||
[Moq1100](./Moq1100.md) | Callback signature must match the signature of the mocked method | ||
[Moq1101](./Moq1101.md) | SetupGet/SetupSet should be used for properties, not for methods | ||
[Moq1200](./Moq1200.md) | Setup should be used only for overridable members | ||
[Moq1201](./Moq1201.md) | Setup of async methods should use `.ReturnsAsync` instance instead of `.Result` | ||
[Moq1300](./Moq1300.md) | `Mock.As()` should take interfaces only |