Skip to content

Commit f19dd54

Browse files
v1.5.0 (#18)
New rules: - **PosInfoMoq2004**: Check that no arguments is passed to mocked interface (fixes #14). - **PosInfoMoq2005**: Check the arguments passed to mocked abstract classes (fixes #15). - **PosInfoMoq2006**: Check setups with Protected() mocks (fixes #10 and fixes #11). Improvements/fixes: - The **PosInfoMoq2000** rule does not raise an error when the `Returns()`/`ReturnsAsync()` methods is invalided by the compiler (fixes #16). - The **PosInfoMoq2001** rule check that each chained member access in a `Setup()` method is overridable (fixes #17). Internal: - Propagate the cancellation token to allow to cancel analysis if the host (the compiler for example) cancel the analysis process. - Migrates the Azure Pipelines to Github actions (fixes #9).
1 parent 6880a50 commit f19dd54

34 files changed

Lines changed: 2001 additions & 230 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Continuous Integration
2+
3+
on:
4+
pull_request:
5+
branches: [ "main" ]
6+
push:
7+
branches: [ "releases/**" ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
15+
- name: Setup .NET 8.x
16+
uses: actions/setup-dotnet@v4
17+
with:
18+
dotnet-version: '8.x'
19+
20+
- name: Build
21+
run: dotnet build --property:Configuration=Debug "PosInformatique.Moq.Analyzers.sln"
22+
23+
- name: Test with the dotnet CLI
24+
run: dotnet test --property:Configuration=Debug "PosInformatique.Moq.Analyzers.sln"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
VersionPrefix:
7+
type: string
8+
description: The version of the library
9+
required: true
10+
default: 1.5.0
11+
VersionSuffix:
12+
type: string
13+
description: The version suffix of the library (for example rc.1)
14+
15+
run-name: ${{ inputs.VersionPrefix }}-${{ inputs.VersionSuffix }}
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Setup .NET 8.x
24+
uses: actions/setup-dotnet@v4
25+
with:
26+
dotnet-version: '8.x'
27+
28+
- name: Build
29+
run: dotnet pack
30+
--property:Configuration=Release
31+
--property:VersionPrefix=${{ github.event.inputs.VersionPrefix }}
32+
--property:VersionSuffix=${{ github.event.inputs.VersionSuffix }}
33+
"src/Moq.Analyzers/Moq.Analyzers.csproj"
34+
35+
- name: Publish the package to nuget.org
36+
run: dotnet nuget push "src/Moq.Analyzers/bin/Release/*.nupkg" --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json

PosInformatique.Moq.Analyzers.sln

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1818
stylecop.json = stylecop.json
1919
EndProjectSection
2020
EndProject
21-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{4C3E1C72-8977-4ABD-8360-A8707DBBA5AE}"
22-
ProjectSection(SolutionItems) = preProject
23-
build\azure-pipelines-ci.yaml = build\azure-pipelines-ci.yaml
24-
build\azure-pipelines-release.yaml = build\azure-pipelines-release.yaml
25-
EndProjectSection
26-
EndProject
2721
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DC2CDF77-88A9-490D-84ED-34832943104A}"
2822
ProjectSection(SolutionItems) = preProject
2923
tests\.editorconfig = tests\.editorconfig
@@ -43,8 +37,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation"
4337
docs\Compilation\PosInfoMoq2001.md = docs\Compilation\PosInfoMoq2001.md
4438
docs\Compilation\PosInfoMoq2002.md = docs\Compilation\PosInfoMoq2002.md
4539
docs\Compilation\PosInfoMoq2003.md = docs\Compilation\PosInfoMoq2003.md
40+
docs\Compilation\PosInfoMoq2004.md = docs\Compilation\PosInfoMoq2004.md
41+
docs\Compilation\PosInfoMoq2005.md = docs\Compilation\PosInfoMoq2005.md
42+
docs\Compilation\PosInfoMoq2006.md = docs\Compilation\PosInfoMoq2006.md
4643
EndProjectSection
4744
EndProject
45+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moq.Analyzers.Sandbox", "tests\Moq.Analyzers.Sandbox\Moq.Analyzers.Sandbox.csproj", "{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}"
46+
EndProject
4847
Global
4948
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5049
Debug|Any CPU = Debug|Any CPU
@@ -59,12 +58,15 @@ Global
5958
{1962BEF9-E6DF-4485-A113-E255C84177D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
6059
{1962BEF9-E6DF-4485-A113-E255C84177D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
6160
{1962BEF9-E6DF-4485-A113-E255C84177D4}.Release|Any CPU.Build.0 = Release|Any CPU
61+
{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62+
{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
63+
{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
64+
{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}.Release|Any CPU.Build.0 = Release|Any CPU
6265
EndGlobalSection
6366
GlobalSection(SolutionProperties) = preSolution
6467
HideSolutionNode = FALSE
6568
EndGlobalSection
6669
GlobalSection(NestedProjects) = preSolution
67-
{4C3E1C72-8977-4ABD-8360-A8707DBBA5AE} = {1D59B801-B4D3-44FC-A2BE-F2F53AC54061}
6870
{DC2CDF77-88A9-490D-84ED-34832943104A} = {1D59B801-B4D3-44FC-A2BE-F2F53AC54061}
6971
{3C20D95F-AB5F-44EC-8FB6-CB9827B7FD63} = {1D59B801-B4D3-44FC-A2BE-F2F53AC54061}
7072
{815BE8D0-C7D5-4B4E-82E0-DE29C11F258E} = {3C20D95F-AB5F-44EC-8FB6-CB9827B7FD63}

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ All the rules of this category should not be disabled (or changed their severity
4141
| [PosInfoMoq2001: The `Setup()` method must be used only on overridable members](docs/Compilation/PosInfoMoq2001.md)) | The `Setup()` method must be applied only for overridable members. |
4242
| [PosInfoMoq2002: `Mock<T>` class can be used only to mock non-sealed class](docs/Compilation/PosInfoMoq2002.md) | The `Mock<T>` can mock only interfaces or non-`sealed` classes. |
4343
| [PosInfoMoq2003: The `Callback()` delegate expression must match the signature of the mocked method](docs/Compilation/PosInfoMoq2003.md) | The delegate in the argument of the `Callback()` method must match the signature of the mocked method. |
44+
| [PosInfoMoq2004: Constructor arguments cannot be passed for interface mocks](docs/Compilation/PosInfoMoq2004.md) | No arguments can be passed to a mocked interface. |
45+
| [PosInfoMoq2005: Constructor arguments must match the constructors of the mocked class](docs/Compilation/PosInfoMoq2005.md) | When instantiating a `Mock<T>`, the parameters must match one of the constructors of the mocked type. |
46+
| [PosInfoMoq2006: The Protected().Setup() method must be use with overridable protected or internal methods](docs/Compilation/PosInfoMoq2006.md) | When using the `Protected().Setup()` configuration, the method mocked must be overridable and protected or internal. |
4447

build/azure-pipelines-ci.yaml

Lines changed: 0 additions & 24 deletions
This file was deleted.

build/azure-pipelines-release.yaml

Lines changed: 0 additions & 54 deletions
This file was deleted.

docs/Compilation/PosInfoMoq2004.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# PosInfoMoq2004: The `Callback()` delegate expression must match the signature of the mocked method
2+
3+
| Property | Value |
4+
|-------------------------------------|---------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2004 |
6+
| **Title** | Constructor arguments cannot be passed for interface mocks. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
Constructor arguments has been passed to a mocked interface.
13+
14+
## Rule description
15+
16+
It is not possible to pass contructor arguments to mocked interface. Is only possible with non-sealed class
17+
or abstract class.
18+
19+
```csharp
20+
[Fact]
21+
public void Test()
22+
{
23+
var service1 = new Mock<IService>("Argument 1", 2); // No constructor arguments can be passed to mocked interface.
24+
var service2 = new Mock<IService>(MockBehavior.Strict, "Argument 1", 2); // Same if we use the MockBehavior.
25+
}
26+
27+
public interface IService
28+
{
29+
}
30+
```
31+
32+
## How to fix violations
33+
34+
To fix a violation of this rule, be sure to pass parameters to mocked abstract class.
35+
36+
## When to suppress warnings
37+
38+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `MoqException`
39+
thrown with the *"Constructor arguments cannot be passed for interface mocks."* message.

docs/Compilation/PosInfoMoq2005.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# PosInfoMoq2005: The `Callback()` delegate expression must match the signature of the mocked method
2+
3+
| Property | Value |
4+
|-------------------------------------|-------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2005 |
6+
| **Title** | Constructor arguments must match the constructors of the mocked class. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
Constructor arguments must match the constructors of the mocked class.
13+
14+
## Rule description
15+
16+
When configurate mock, all the arguments must match one of the constructor of the mocked type.
17+
18+
```csharp
19+
[Fact]
20+
public void Test()
21+
{
22+
var service1 = new Mock<Service>(1, 2, 3); // The arguments does not match one of the Service type constructors.
23+
var service2 = new Mock<Service>("Argument 1", 2); // OK
24+
var service3 = new Mock<Service>(MockBehavior.Strict, "Argument 1", 2); // OK
25+
}
26+
27+
public abstract class Service
28+
{
29+
public Service(string a)
30+
{
31+
}
32+
33+
public Service(string a, int b)
34+
{
35+
}
36+
}
37+
```
38+
39+
## How to fix violations
40+
41+
To fix a violation of this rule, be sure to pass right arguments of one of the constructor of the mocked instance.
42+
43+
## When to suppress warnings
44+
45+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `MoqException`
46+
thrown with the *"Constructor arguments must match the constructors of the mocked class."* message.

docs/Compilation/PosInfoMoq2006.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# PosInfoMoq2006: The Protected().Setup() method must be use with overridable protected or internal methods
2+
3+
| Property | Value |
4+
|-------------------------------------|----------------------------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2006 |
6+
| **Title** | The `Protected().Setup()` method must be use with overridable protected or internal methods. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
A `Protected().Setup()` reference a method in the mocked type which is:
13+
- Not existing in the mocked type.
14+
- Not overridable (`abstract`, `virtual` or `override`, but not `sealed`).
15+
- Not `protected` or `internal`.
16+
17+
## Rule description
18+
19+
When using the `Protected().Setup()`, the method mocked must be `protected`, `internal` or `protected internal`,
20+
and must be overridable (`virtual`, `abstract` and `override`, but not `sealed`).
21+
22+
```csharp
23+
[Fact]
24+
public void Test()
25+
{
26+
var service = new Mock<Service>(1, 2, 3);
27+
service.Protected().Setup("GetData") // The GetData() is public and can be mocked with Protected() feature.
28+
.Returns(10);
29+
service.Protected().Setup("NotExists") // The NotExists() method does not exist.
30+
.Returns(10);
31+
service.Protected().Setup("YouCantOverrideMe") // The YouCantOverrideMe() is not virtual or abstract.
32+
.Returns(10);
33+
}
34+
35+
public abstract class Service
36+
{
37+
public abstract int GetData();
38+
39+
protected void YouCantOverrideMe() { };
40+
}
41+
```
42+
43+
## How to fix violations
44+
45+
To fix a violation of this rule, use the `Protected().Setup()` to mock method which are:
46+
- `protected`
47+
- `internal`
48+
- `protected internal`
49+
- Overridable (`virtual`, `abstract` or `override`, but not `sealed`).
50+
51+
Else use the standard mocking feature without the `Protected()` method.
52+
53+
## When to suppress warnings
54+
55+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `MoqException`
56+
thrown with the *"Method X.xxxx is public. Use strong-typed."* message.

src/Moq.Analyzers/AnalyzerReleases.Shipped.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
## Release 1.3.0
1+
## Release 1.5.0
2+
3+
### New Rules
4+
5+
Rule ID | Category | Severity | Notes
6+
--------|----------|----------|--------------------
7+
PosInfoMoq2004 | Compilation | Error | Constructor arguments cannot be passed for interface mocks.
8+
PosInfoMoq2005 | Compilation | Error | Constructor arguments must match the constructors of the mocked class.
9+
PosInfoMoq2006 | Compilation | Error | The `Protected().Setup()` method must be use with overridable protected or internal methods.
10+
11+
## Release 1.3.0
212

313
### New Rules
414

0 commit comments

Comments
 (0)