Skip to content

Commit

Permalink
v1.11.0 (#43)
Browse files Browse the repository at this point in the history
* 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().
  • Loading branch information
GillesTourreau authored Oct 28, 2024
1 parent dd853ba commit 2a5b492
Show file tree
Hide file tree
Showing 34 changed files with 1,476 additions and 461 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/github-actions-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
type: string
description: The version of the library
required: true
default: 1.10.0
default: 1.11.0
VersionSuffix:
type: string
description: The version suffix of the library (for example rc.1)
Expand Down
4 changes: 4 additions & 0 deletions PosInformatique.Moq.Analyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Design", "Design", "{815BE8
docs\Design\PosInfoMoq1002.md = docs\Design\PosInfoMoq1002.md
docs\Design\PosInfoMoq1003.md = docs\Design\PosInfoMoq1003.md
docs\Design\PosInfoMoq1004.md = docs\Design\PosInfoMoq1004.md
docs\Design\PosInfoMoq1005.md = docs\Design\PosInfoMoq1005.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation", "{D9C84D36-7F9C-4EFB-BE6F-9F7A05FE957D}"
Expand All @@ -51,6 +52,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation"
docs\Compilation\PosInfoMoq2011.md = docs\Compilation\PosInfoMoq2011.md
docs\Compilation\PosInfoMoq2012.md = docs\Compilation\PosInfoMoq2012.md
docs\Compilation\PosInfoMoq2013.md = docs\Compilation\PosInfoMoq2013.md
docs\Compilation\PosInfoMoq2014.md = docs\Compilation\PosInfoMoq2014.md
docs\Compilation\PosInfoMoq2015.md = docs\Compilation\PosInfoMoq2015.md
docs\Compilation\PosInfoMoq2016.md = docs\Compilation\PosInfoMoq2016.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Moq.Analyzers.Sandbox", "tests\Moq.Analyzers.Sandbox\Moq.Analyzers.Sandbox.csproj", "{07F970A1-1477-4D4C-B233-C9B4DA6E3AD6}"
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Design rules used to make your unit tests more strongly strict.
| [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. |
| [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. |
| [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. |
| [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. |

### Compilation

Expand All @@ -40,7 +41,7 @@ All the rules of this category should not be disabled (or changed their severity
| Rule | Description |
| - | - |
| [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. |
| [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. |
| [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. |
| [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. |
| [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. |
| [PosInfoMoq2004: Constructor arguments cannot be passed for interface mocks](docs/Compilation/PosInfoMoq2004.md) | No arguments can be passed to a mocked interface. |
Expand All @@ -53,6 +54,9 @@ All the rules of this category should not be disabled (or changed their severity
| [PosInfoMoq2011: Constructor of the mocked class must be accessible.](docs/Compilation/PosInfoMoq2011.md) | The constructor of the instantiate mocked class must non-private. |
| [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. |
| [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. |
| [PosInfoMoq2014: The `Callback()` delegate expression must not return a value.](docs/Compilation/PosInfoMoq2014.md) | The `Callback()` delegate expression must not return a value. |
| [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. |
| [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. |



27 changes: 16 additions & 11 deletions docs/Compilation/PosInfoMoq2001.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# PosInfoMoq2001: The `Setup()` method must be used only on overridable members
# PosInfoMoq2001: The `Setup()`/`SetupSet()` method must be used only on overridable members

| Property | Value |
|-------------------------------------|---------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2001 |
| **Title** | The `Setup()` method must be used only on overridable members |
| **Title** | The `Setup()`/`SetupSet()` method must be used only on overridable members |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

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

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

For example, the following methods and properties can be mock and used in the `Setup()` method:
- `IService.MethodCanBeMocked()`
- `IService.PropertyCanBeMocked`
- `Service.VirtualMethodCanBeMocked`
- `Service.VirtualPropertyCanBeMocked`
- `Service.AbstractMethodCanBeMocked`
- `Service.AbstractPropertyCanBeMocked`
For example:
- The following methods and properties can be mock and used in the `Setup()` method:
- `IService.MethodCanBeMocked()`
- `IService.PropertyCanBeMocked`
- `Service.VirtualMethodCanBeMocked`
- `Service.VirtualPropertyCanBeMocked`
- `Service.AbstractMethodCanBeMocked`
- `Service.AbstractPropertyCanBeMocked`
- The following properties can be mock and used in the `SetupSet()` method:
- `IService.PropertyCanBeMocked`
- `Service.VirtualPropertyCanBeMocked`
- `Service.AbstractPropertyCanBeMocked`

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

## How to fix violations

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

## When to suppress warnings

Expand Down
2 changes: 1 addition & 1 deletion docs/Compilation/PosInfoMoq2006.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and must be overridable (`virtual`, `abstract` and `override`, but not `sealed`)
public void Test()
{
var service = new Mock<Service>();
service.Protected().Setup("GetData") // The GetData() is public and can be mocked with Protected() feature.
service.Protected().Setup("GetData") // The GetData() is public and can't be mocked with Protected() feature.
.Returns(10);
service.Protected().Setup("NotExists") // The NotExists() method does not exist.
.Returns(10);
Expand Down
45 changes: 45 additions & 0 deletions docs/Compilation/PosInfoMoq2014.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# PosInfoMoq2014: The `Callback()` delegate expression must not return a value.

| Property | Value |
|-------------------------------------|-----------------------------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2014 |
| **Title** | The `Callback()` delegate expression must not return a value. |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

The delegate in the argument of the `Callback()` method must not return a value.

## Rule description

The lambda expression in the argument of the `Callback()` method must not return a value.

```csharp
[Fact]
public void Test()
{
var service = new Mock<Service>();
service.Setup(s => s.GetData("TOURREAU", 1234))
.Callback((string n, int age) =>
{
// ...
return 1234; // The delegate in the Callback() method must not return a value.
})
.Returns(10);
}

public interface IService
{
public int GetData(string name, int age) { }
}
```

## How to fix violations

To fix a violation of this rule, be sure that the delegate method in the `Callback()` method does not return a value.

## 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 `ArgumentException`
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.
50 changes: 50 additions & 0 deletions docs/Compilation/PosInfoMoq2015.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# PosInfoMoq2015: The `Protected().Setup()` method must match the return type of the mocked method

| Property | Value |
|-------------------------------------|----------------------------------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2015 |
| **Title** | The `Protected().Setup()` method must match the return type of the mocked method. |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

The method setup with `Protected().Setup()` must match the return type of the mocked method.

## Rule description

When using the `Protected().Setup()`, the return type of mocked method must match of the generic
argument specified in the `Setup<T>()` method.

```csharp
[Fact]
public void Test()
{
var service = new Mock<Service>();
service.Protected().Setup<int>("GetData") // OK.
.Returns(10);
service.Protected().Setup("GetData") // Error: The GetData() return an int, Setup<int>() must be use.
service.Protected().Setup<int>("SendEmail") // Error: The SendEmail() method does not return a value, the `int` generic argument must be remove.0
.Returns(10);
service.Protected().Setup<string>("GetData") // Error: The GetData() return an int, Setup<int>() must be use.
.Returns("The data");
}

public abstract class Service
{
protected abstract int GetData();

protected abstract void SendEmail();
}
```

## How to fix violations

To fix a violation of this rule, use the generic parameter of the `Setup<T>()` method if the protected mocked
method return a value. Else do not specify a generic parameter for the `Setup<T>()` method of the protected mocked
method does not return a value (`void`).

## 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 `ArgumentException`
thrown with the *"Can't set return value for void method xxx."* message.
45 changes: 45 additions & 0 deletions docs/Compilation/PosInfoMoq2016.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# PosInfoMoq2016: `Mock<T>` constructor with factory lambda expression can be used only with classes.

| Property | Value |
|-------------------------------------|-------------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2016 |
| **Title** | `Mock<T>` constructor with factory lambda expression can be used only with classes. |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

The factory lambda expression used in `Mock<T>` instantiation must used only for the classes.

## Rule description

When using a lambda expression in the constructor of Mock<T> to create a mock instance, the mocked type must be a class.

```csharp
[Fact]
public void Test()
{
var service1 = new Mock<IService>(() => new Service()); // The factory lambda expression can be used only on classes type.
var service2 = new Mock<Service>(() => new Service()); // OK
}

public interface IService
{
}

public class Service : IService:
{
public Service(string a)
{
}
}
```

## How to fix violations

To fix a violation of this rule, ensure that the lambda expression factory is used with a mocked type that is a class.

## 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 `ArgumentException`
thrown with the *"Constructor arguments cannot be passed for interface mocks."* message.
Binary file added docs/Design/PosInfoMoq1005-Fixer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions docs/Design/PosInfoMoq1005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# PosInfoMoq1005: Defines the generic argument of the `SetupSet()` method with the type of the mocked property.

| Property | Value |
|-------------------------------------|------------------------------------------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq1005 |
| **Title** | Defines the generic argument of the `SetupSet()` method with the type of the mocked property. |
| **Category** | Design |
| **Default severity** | Warning |

## Cause

A property setter has been set up using `SetupSet()` without a generic argument that represents the type of the mocked property.

## Rule description

Moq provides two methods to mock a property setter:
- `Mock<T>.SetupSet(Action<T>)`
- `Mock<T>.SetupSet<TProperty>(Action<T, TProperty>)`

When setting up a property setter, use `Mock<T>.SetupSet<TProperty>(Action<T, TProperty>)` by explicitly defining the type of the property to mock.
This overload of the `SetupSet()` method allows you to define a typed `Callback()` and avoid exceptions if the delegate argument in the `Callback()`
does not match the property type.

For example, consider the following code to test:

```csharp
[Fact]
public interface Customer
{
string Name { get; set; }
}
```

If you mock the setter of the `Customer.Name` property, you should set up the property with the `SetupSet<string>()` method:

```csharp
[Fact]
public void SetNameOfCustomer()
{
var customer = new Mock<Customer>();
customer.SetupSet<string>(c => c.Name = "Gilles") // The SetupSet<string>() version is used.
.Callback((string value) =>
{
// Called when the setter of the property is set.
});
}
```

The following code violates the rule because the `SetupSet()` method has no generic argument:

```csharp
[Fact]
public void SetNameOfCustomer()
{
var customer = new Mock<Customer>();
customer.SetupSet(c => c.Name = "Gilles") // The SetupSet() has been used without set the generic argument.
.Callback((string value) =>
{
// Called when the setter of the property is set.
});
}
```

If the non-generic version of the `SetupSet()` method is used, the delegate in the `Callback()` method cannot be checked at compile time,
an exception will occur during the execution of the unit test:

```csharp
[Fact]
public void SetNameOfCustomer()
{
var customer = new Mock<Customer>();
customer.SetupSet(c => c.Name = "Gilles")
.Callback((int value) => // The code compiles, but during the execution of the unit test
{ // an ArgumentException will be thrown.
});
}
```

## How to fix violations

To fix a violation of this rule, use the `SetupSet<TProperty>()` method with the type of the mocked property as the generic argument.

### Visual Studio fixer
A Visual Studio fixer exists to set explicitly the generic argument of the `SetupSet<T>()` method with the property type
in the current document, project or solution.

![Visual Studio rule fixer](PosInfoMoq1005-Fixer.png)

## When to suppress warnings

Do not suppress a warning from this rule. Using the `SetupSet<T>()` method ensures that the delegate argument in the `Callback()`
method matches the type of the property.
12 changes: 11 additions & 1 deletion src/Moq.Analyzers/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
## Release 1.10.0
## Release 1.11.0

### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
PosInfoMoq1005 | Design | Warning | Defines the generic argument of the `SetupSet()` method with the type of the mocked property.
PosInfoMoq2014 | Compilation | Error | The `Callback()` delegate expression must not return a value.
PosInfoMoq2015 | Compilation | Error | The `Protected().Setup()` method must match the return type of the mocked method.
PosInfoMoq2016 | Compilation | Error | `Mock<T>` constructor with factory lambda expression can be used only with classes.

## Release 1.10.0

### New Rules
Rule ID | Category | Severity | Notes
Expand Down
Loading

0 comments on commit 2a5b492

Please sign in to comment.