Skip to content

Commit 2a5b492

Browse files
v1.11.0 (#43)
* Fix the documentation link of the PosInfoMoq2013 rule. * Add a new PosInfoMoq2014 rule to check if a delegate in the Callback() method does not a return a value (fixes: #35). * Add the PosInfoMoq2015 rule to check the return type of the Protected() setup methods (fixes #38). * Fix the PosInfoMoq1001 rule to support Mock<T> instantiation with lambda expression argument (fixes #39). * Fix the PosInfoMoq2004 to not check the Mock<T> instantiation with the factory lambda expression (fixes: #39). * Add the PosInfoMoq2016 rule to check the lambda expression factory are used only with class types (fixes: #40). * Merge the PosInfoMoq2004 rule to the ConstructorArgumentsMustMatchAnalyzer analyzer. * Rename the ConstructorArgumentsAnalyzer. * Fix the BehaviorStrict fixer with Mock.Of<T>() when using in constructors (fixes #41). * Add the PosInfoMoq1005 rule to check usage of the SetupSet<T>() method (fixes #42). * Updates the PosInfoMoq2001 to check property in the SetupSet().
1 parent dd853ba commit 2a5b492

34 files changed

Lines changed: 1476 additions & 461 deletions

.github/workflows/github-actions-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
type: string
88
description: The version of the library
99
required: true
10-
default: 1.10.0
10+
default: 1.11.0
1111
VersionSuffix:
1212
type: string
1313
description: The version suffix of the library (for example rc.1)

PosInformatique.Moq.Analyzers.sln

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Design", "Design", "{815BE8
3333
docs\Design\PosInfoMoq1002.md = docs\Design\PosInfoMoq1002.md
3434
docs\Design\PosInfoMoq1003.md = docs\Design\PosInfoMoq1003.md
3535
docs\Design\PosInfoMoq1004.md = docs\Design\PosInfoMoq1004.md
36+
docs\Design\PosInfoMoq1005.md = docs\Design\PosInfoMoq1005.md
3637
EndProjectSection
3738
EndProject
3839
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation", "{D9C84D36-7F9C-4EFB-BE6F-9F7A05FE957D}"
@@ -51,6 +52,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation"
5152
docs\Compilation\PosInfoMoq2011.md = docs\Compilation\PosInfoMoq2011.md
5253
docs\Compilation\PosInfoMoq2012.md = docs\Compilation\PosInfoMoq2012.md
5354
docs\Compilation\PosInfoMoq2013.md = docs\Compilation\PosInfoMoq2013.md
55+
docs\Compilation\PosInfoMoq2014.md = docs\Compilation\PosInfoMoq2014.md
56+
docs\Compilation\PosInfoMoq2015.md = docs\Compilation\PosInfoMoq2015.md
57+
docs\Compilation\PosInfoMoq2016.md = docs\Compilation\PosInfoMoq2016.md
5458
EndProjectSection
5559
EndProject
5660
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moq.Analyzers.Sandbox", "tests\Moq.Analyzers.Sandbox\Moq.Analyzers.Sandbox.csproj", "{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}"

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Design rules used to make your unit tests more strongly strict.
3131
| [PosInfoMoq1002: `Verify()` methods should be called when `Verifiable()` has been setup](docs/Design/PosInfoMoq1002.md) | When a mocked member has been setup with the `Verifiable()` method, the `Verify()` method must be called at the end of the unit test. |
3232
| [PosInfoMoq1003: The `Callback()` method should be used to check the parameters when mocking a method with `It.IsAny<T>()` arguments](docs/Design/PosInfoMoq1003.md) | When a mocked method contains a `It.IsAny<T>()` argument, the related parameter should be checked in the `Callback()` method. |
3333
| [PosInfoMoq1004: The `Callback()` parameter should not be ignored if it has been setup as an `It.IsAny<T>()` argument](docs/Design/PosInfoMoq1004.md) | When a mocked method contains a `It.IsAny<T>()` argument, the related parameter should not be ignored in the `Callback()` method. |
34+
| [PosInfoMoq1005: Defines the generic argument of the `SetupSet()` method with the type of the mocked property](docs/Design/PosInfoMoq1005.md) | When mocking the setter of a property, use the `SetupSet<TProperty>()` method version. |
3435

3536
### Compilation
3637

@@ -40,7 +41,7 @@ All the rules of this category should not be disabled (or changed their severity
4041
| Rule | Description |
4142
| - | - |
4243
| [PosInfoMoq2000: The `Returns()` or `ReturnsAsync()` methods must be call for Strict mocks](docs/Compilation/PosInfoMoq2000.md) | When a `Mock<T>` has been defined with the `Strict` behavior, the `Returns()` or `ReturnsAsync()` method must be called when setup a method to mock which returns a value. |
43-
| [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. |
44+
| [PosInfoMoq2001: The `Setup()`/`SetupSet()` method must be used only on overridable members](docs/Compilation/PosInfoMoq2001.md)) | The `Setup()` method must be applied only for overridable members. |
4445
| [PosInfoMoq2002: `Mock<T>` class can be used only to mock non-sealed class](docs/Compilation/PosInfoMoq2002.md) | The `Mock<T>` class can mock only interfaces or non-`sealed` classes. |
4546
| [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. |
4647
| [PosInfoMoq2004: Constructor arguments cannot be passed for interface mocks](docs/Compilation/PosInfoMoq2004.md) | No arguments can be passed to a mocked interface. |
@@ -53,6 +54,9 @@ All the rules of this category should not be disabled (or changed their severity
5354
| [PosInfoMoq2011: Constructor of the mocked class must be accessible.](docs/Compilation/PosInfoMoq2011.md) | The constructor of the instantiate mocked class must non-private. |
5455
| [PosInfoMoq2012: The delegate in the argument of the `Returns()` method must return a value with same type of the mocked method.](docs/Compilation/PosInfoMoq2012.md) | The lambda expression, anonymous method or method in the argument of the `Returns()` must return return a value of the same type as the mocked method or property. |
5556
| [PosInfoMoq2013: The delegate in the argument of the `Returns()`/`ReturnsAsync()` method must have the same parameter types of the mocked method/property.](docs/Compilation/PosInfoMoq2013.md) | The lambda expression, anonymous method or method in the argument of the `Returns()`/`ReturnsAsync()` must have the same arguments type of the mocked method or property. |
57+
| [PosInfoMoq2014: The `Callback()` delegate expression must not return a value.](docs/Compilation/PosInfoMoq2014.md) | The `Callback()` delegate expression must not return a value. |
58+
| [PosInfoMoq2015: The `Protected().Setup()` method must match the return type of the mocked method](docs/Compilation/PosInfoMoq2015.md) | The method setup with `Protected().Setup()` must match the return type of the mocked method. |
59+
| [PosInfoMoq2016: `Mock<T>` constructor with factory lambda expression can be used only with classes.](docs/Compilation/PosInfoMoq2016.md) | The factory lambda expression used in `Mock<T>` instantiation must used only for the classes. |
5660

5761

5862

docs/Compilation/PosInfoMoq2001.md

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
# PosInfoMoq2001: The `Setup()` method must be used only on overridable members
1+
# PosInfoMoq2001: The `Setup()`/`SetupSet()` method must be used only on overridable members
22

33
| Property | Value |
44
|-------------------------------------|---------------------------------------------------------------|
55
| **Rule ID** | PosInfoMoq2001 |
6-
| **Title** | The `Setup()` method must be used only on overridable members |
6+
| **Title** | The `Setup()`/`SetupSet()` method must be used only on overridable members |
77
| **Category** | Compilation |
88
| **Default severity** | Error |
99

1010
## Cause
1111

12-
The `Setup()` method must be applied only for overridable members.
12+
The `Setup()` or `SetupSet()` methods must be applied only for overridable members.
1313
An overridable member is a **method** or **property** which is in:
1414
- An `interface`.
1515
- A non-`sealed` `class`. In this case, the member must be:
@@ -20,13 +20,18 @@ An overridable member is a **method** or **property** which is in:
2020

2121
The `Setup()` method must be applied only for overridable members.
2222

23-
For example, the following methods and properties can be mock and used in the `Setup()` method:
24-
- `IService.MethodCanBeMocked()`
25-
- `IService.PropertyCanBeMocked`
26-
- `Service.VirtualMethodCanBeMocked`
27-
- `Service.VirtualPropertyCanBeMocked`
28-
- `Service.AbstractMethodCanBeMocked`
29-
- `Service.AbstractPropertyCanBeMocked`
23+
For example:
24+
- The following methods and properties can be mock and used in the `Setup()` method:
25+
- `IService.MethodCanBeMocked()`
26+
- `IService.PropertyCanBeMocked`
27+
- `Service.VirtualMethodCanBeMocked`
28+
- `Service.VirtualPropertyCanBeMocked`
29+
- `Service.AbstractMethodCanBeMocked`
30+
- `Service.AbstractPropertyCanBeMocked`
31+
- The following properties can be mock and used in the `SetupSet()` method:
32+
- `IService.PropertyCanBeMocked`
33+
- `Service.VirtualPropertyCanBeMocked`
34+
- `Service.AbstractPropertyCanBeMocked`
3035

3136
```csharp
3237
public interface IService
@@ -53,7 +58,7 @@ static methods which can not be overriden.
5358

5459
## How to fix violations
5560

56-
To fix a violation of this rule, be sure to mock a member in the `Setup()` method which can be overriden.
61+
To fix a violation of this rule, be sure to mock a member in the `Setup()` or `SetupSet()` method which can be overriden.
5762

5863
## When to suppress warnings
5964

docs/Compilation/PosInfoMoq2006.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ and must be overridable (`virtual`, `abstract` and `override`, but not `sealed`)
2424
public void Test()
2525
{
2626
var service = new Mock<Service>();
27-
service.Protected().Setup("GetData") // The GetData() is public and can be mocked with Protected() feature.
27+
service.Protected().Setup("GetData") // The GetData() is public and can't be mocked with Protected() feature.
2828
.Returns(10);
2929
service.Protected().Setup("NotExists") // The NotExists() method does not exist.
3030
.Returns(10);

docs/Compilation/PosInfoMoq2014.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# PosInfoMoq2014: The `Callback()` delegate expression must not return a value.
2+
3+
| Property | Value |
4+
|-------------------------------------|-----------------------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2014 |
6+
| **Title** | The `Callback()` delegate expression must not return a value. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
The delegate in the argument of the `Callback()` method must not return a value.
13+
14+
## Rule description
15+
16+
The lambda expression in the argument of the `Callback()` method must not return a value.
17+
18+
```csharp
19+
[Fact]
20+
public void Test()
21+
{
22+
var service = new Mock<Service>();
23+
service.Setup(s => s.GetData("TOURREAU", 1234))
24+
.Callback((string n, int age) =>
25+
{
26+
// ...
27+
return 1234; // The delegate in the Callback() method must not return a value.
28+
})
29+
.Returns(10);
30+
}
31+
32+
public interface IService
33+
{
34+
public int GetData(string name, int age) { }
35+
}
36+
```
37+
38+
## How to fix violations
39+
40+
To fix a violation of this rule, be sure that the delegate method in the `Callback()` method does not return a value.
41+
42+
## When to suppress warnings
43+
44+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `ArgumentException`
45+
thrown with the *"Invalid callback. This overload of the "Callback" method only accepts "void" (C#) or "Sub" (VB.NET) delegates with parameter types matching those of the set up method. (Parameter 'callback')"* message.

docs/Compilation/PosInfoMoq2015.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# PosInfoMoq2015: The `Protected().Setup()` method must match the return type of the mocked method
2+
3+
| Property | Value |
4+
|-------------------------------------|----------------------------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2015 |
6+
| **Title** | The `Protected().Setup()` method must match the return type of the mocked method. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
The method setup with `Protected().Setup()` must match the return type of the mocked method.
13+
14+
## Rule description
15+
16+
When using the `Protected().Setup()`, the return type of mocked method must match of the generic
17+
argument specified in the `Setup<T>()` method.
18+
19+
```csharp
20+
[Fact]
21+
public void Test()
22+
{
23+
var service = new Mock<Service>();
24+
service.Protected().Setup<int>("GetData") // OK.
25+
.Returns(10);
26+
service.Protected().Setup("GetData") // Error: The GetData() return an int, Setup<int>() must be use.
27+
service.Protected().Setup<int>("SendEmail") // Error: The SendEmail() method does not return a value, the `int` generic argument must be remove.0
28+
.Returns(10);
29+
service.Protected().Setup<string>("GetData") // Error: The GetData() return an int, Setup<int>() must be use.
30+
.Returns("The data");
31+
}
32+
33+
public abstract class Service
34+
{
35+
protected abstract int GetData();
36+
37+
protected abstract void SendEmail();
38+
}
39+
```
40+
41+
## How to fix violations
42+
43+
To fix a violation of this rule, use the generic parameter of the `Setup<T>()` method if the protected mocked
44+
method return a value. Else do not specify a generic parameter for the `Setup<T>()` method of the protected mocked
45+
method does not return a value (`void`).
46+
47+
## When to suppress warnings
48+
49+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `ArgumentException`
50+
thrown with the *"Can't set return value for void method xxx."* message.

docs/Compilation/PosInfoMoq2016.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# PosInfoMoq2016: `Mock<T>` constructor with factory lambda expression can be used only with classes.
2+
3+
| Property | Value |
4+
|-------------------------------------|-------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq2016 |
6+
| **Title** | `Mock<T>` constructor with factory lambda expression can be used only with classes. |
7+
| **Category** | Compilation |
8+
| **Default severity** | Error |
9+
10+
## Cause
11+
12+
The factory lambda expression used in `Mock<T>` instantiation must used only for the classes.
13+
14+
## Rule description
15+
16+
When using a lambda expression in the constructor of Mock<T> to create a mock instance, the mocked type must be a class.
17+
18+
```csharp
19+
[Fact]
20+
public void Test()
21+
{
22+
var service1 = new Mock<IService>(() => new Service()); // The factory lambda expression can be used only on classes type.
23+
var service2 = new Mock<Service>(() => new Service()); // OK
24+
}
25+
26+
public interface IService
27+
{
28+
}
29+
30+
public class Service : IService:
31+
{
32+
public Service(string a)
33+
{
34+
}
35+
}
36+
```
37+
38+
## How to fix violations
39+
40+
To fix a violation of this rule, ensure that the lambda expression factory is used with a mocked type that is a class.
41+
42+
## When to suppress warnings
43+
44+
Do not suppress an error from this rule. If bypassed, the execution of the unit test will be failed with a `ArgumentException`
45+
thrown with the *"Constructor arguments cannot be passed for interface mocks."* message.
46.4 KB
Loading

docs/Design/PosInfoMoq1005.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# PosInfoMoq1005: Defines the generic argument of the `SetupSet()` method with the type of the mocked property.
2+
3+
| Property | Value |
4+
|-------------------------------------|------------------------------------------------------------------------------------------------------|
5+
| **Rule ID** | PosInfoMoq1005 |
6+
| **Title** | Defines the generic argument of the `SetupSet()` method with the type of the mocked property. |
7+
| **Category** | Design |
8+
| **Default severity** | Warning |
9+
10+
## Cause
11+
12+
A property setter has been set up using `SetupSet()` without a generic argument that represents the type of the mocked property.
13+
14+
## Rule description
15+
16+
Moq provides two methods to mock a property setter:
17+
- `Mock<T>.SetupSet(Action<T>)`
18+
- `Mock<T>.SetupSet<TProperty>(Action<T, TProperty>)`
19+
20+
When setting up a property setter, use `Mock<T>.SetupSet<TProperty>(Action<T, TProperty>)` by explicitly defining the type of the property to mock.
21+
This overload of the `SetupSet()` method allows you to define a typed `Callback()` and avoid exceptions if the delegate argument in the `Callback()`
22+
does not match the property type.
23+
24+
For example, consider the following code to test:
25+
26+
```csharp
27+
[Fact]
28+
public interface Customer
29+
{
30+
string Name { get; set; }
31+
}
32+
```
33+
34+
If you mock the setter of the `Customer.Name` property, you should set up the property with the `SetupSet<string>()` method:
35+
36+
```csharp
37+
[Fact]
38+
public void SetNameOfCustomer()
39+
{
40+
var customer = new Mock<Customer>();
41+
customer.SetupSet<string>(c => c.Name = "Gilles") // The SetupSet<string>() version is used.
42+
.Callback((string value) =>
43+
{
44+
// Called when the setter of the property is set.
45+
});
46+
}
47+
```
48+
49+
The following code violates the rule because the `SetupSet()` method has no generic argument:
50+
51+
```csharp
52+
[Fact]
53+
public void SetNameOfCustomer()
54+
{
55+
var customer = new Mock<Customer>();
56+
customer.SetupSet(c => c.Name = "Gilles") // The SetupSet() has been used without set the generic argument.
57+
.Callback((string value) =>
58+
{
59+
// Called when the setter of the property is set.
60+
});
61+
}
62+
```
63+
64+
If the non-generic version of the `SetupSet()` method is used, the delegate in the `Callback()` method cannot be checked at compile time,
65+
an exception will occur during the execution of the unit test:
66+
67+
```csharp
68+
[Fact]
69+
public void SetNameOfCustomer()
70+
{
71+
var customer = new Mock<Customer>();
72+
customer.SetupSet(c => c.Name = "Gilles")
73+
.Callback((int value) => // The code compiles, but during the execution of the unit test
74+
{ // an ArgumentException will be thrown.
75+
});
76+
}
77+
```
78+
79+
## How to fix violations
80+
81+
To fix a violation of this rule, use the `SetupSet<TProperty>()` method with the type of the mocked property as the generic argument.
82+
83+
### Visual Studio fixer
84+
A Visual Studio fixer exists to set explicitly the generic argument of the `SetupSet<T>()` method with the property type
85+
in the current document, project or solution.
86+
87+
![Visual Studio rule fixer](PosInfoMoq1005-Fixer.png)
88+
89+
## When to suppress warnings
90+
91+
Do not suppress a warning from this rule. Using the `SetupSet<T>()` method ensures that the delegate argument in the `Callback()`
92+
method matches the type of the property.

0 commit comments

Comments
 (0)