Skip to content

Commit

Permalink
v1.9.1 (#28)
Browse files Browse the repository at this point in the history
* Renames the NoSealedClassAnalyzer to MockClassCanBeUsedOnlyToMockNonSealedClassAnalyzer.
* Add the PosInfoMoq2009 rule.
* Updates the PosInfoMoq1001 rule to check the Mock.Of<T>() usage (fixes #27).
* Change the version number to 1.9.1
* Renames the MockOfCanBeUsedOnlyToMockNonSealedClassAnalyzer to MockOfAnalyzer.
* Add the PosInfoMoq2010 rule to check that types for Mock.Of<T>() have parameterless contructor.
* Add the PosInfoMoq2011 rule.
* Fix the PosInfoMoq2005 rule to check only the class types.
  • Loading branch information
GillesTourreau authored Jul 17, 2024
1 parent 282546d commit d639007
Show file tree
Hide file tree
Showing 26 changed files with 1,696 additions and 140 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.8.0
default: 1.9.1
VersionSuffix:
type: string
description: The version suffix of the library (for example rc.1)
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,6 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml

# Specific
/tests/Moq.Analyzers.Sandbox/Sandbox.cs
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 P.O.S Informatique
Copyright (c) 2023-2024 P.O.S Informatique

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
3 changes: 3 additions & 0 deletions PosInformatique.Moq.Analyzers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compilation", "Compilation"
docs\Compilation\PosInfoMoq2006.md = docs\Compilation\PosInfoMoq2006.md
docs\Compilation\PosInfoMoq2007.md = docs\Compilation\PosInfoMoq2007.md
docs\Compilation\PosInfoMoq2008.md = docs\Compilation\PosInfoMoq2008.md
docs\Compilation\PosInfoMoq2009.md = docs\Compilation\PosInfoMoq2009.md
docs\Compilation\PosInfoMoq2010.md = docs\Compilation\PosInfoMoq2010.md
docs\Compilation\PosInfoMoq2011.md = docs\Compilation\PosInfoMoq2011.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
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Design rules used to make your unit tests more strongly strict.
| Rule | Description |
| - | - |
| [PosInfoMoq1000: `VerifyAll()` methods should be called when instantiate a `Mock<T>` instances](docs/Design/PosInfoMoq1000.md) | When instantiating a `Mock<T>` in the *Arrange* phase of an unit test, `VerifyAll()` method should be called in the *Assert* phase to check the setup methods has been called. |
| [PosInfoMoq1001: The `Mock<T>` instance behavior should be defined to Strict mode](docs/Design/PosInfoMoq1001.md) | When instantiating a `Mock<T>` instance, the `MockBehavior` of the `Mock` instance should be defined to `Strict`. |
| [PosInfoMoq1001: The mocked instances behaviors should be defined to `Strict` mode](docs/Design/PosInfoMoq1001.md) | When instantiating a `Mock<T>` instance, the `MockBehavior` of the `Mock` instance should be defined to `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. |
Expand All @@ -41,13 +41,16 @@ All the rules of this category should not be disabled (or changed their severity
| - | - |
| [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. |
| [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. |
| [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. |
| [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. |
| [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. |
| [PosInfoMoq2007: The `As<T>()` method can be used only with interfaces.](docs/Compilation/PosInfoMoq2007.md) | The `As<T>()` can only be use with the interfaces. |
| [PosInfoMoq2008: The `Verify()` method must be used only on overridable members](docs/Compilation/PosInfoMoq2008.md)) | The `Verify()` method must be applied only for overridable members. |
| [PosInfoMoq2009: `Mock.Of<T>` method must be used only to mock non-sealed class](docs/Compilation/PosInfoMoq2009.md) | The `Mock.Of<T>` method can mock only interfaces or non-`sealed` classes |
| [PosInfoMoq2010: `Mock.Of<T>` method must be used only with types that contains parameterless contructor](docs/Compilation/PosInfoMoq2010.md) | The `Mock.Of<T>` method requires a non-private parameterless contructor |
| [PosInfoMoq2011: Constructor of the mocked class must be accessible.](docs/Compilation/PosInfoMoq2011.md) | The constructor of the instantiate mocked class must non-private. |



40 changes: 40 additions & 0 deletions docs/Compilation/PosInfoMoq2009.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# PosInfoMoq2009: `Mock.Of<T>` method must be used only to mock non-sealed class

| Property | Value |
|-------------------------------------|---------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2009 |
| **Title** | `Mock.Of<T>` method must be used only to mock non-sealed class |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

The `Mock.Of<T>` method must be used only to mock non-sealed class.

## Rule description

The `Mock.Of<T>` method must be use only on the interfaces or non-`sealed` classes.

For example, the following code can not mock the `Service` class because it is `sealed`.

```csharp
[Fact]
public void Test()
{
var service = Mock.Of<Service>(s => s.Property == 1234); // The Service can not be mocked, because it is a sealed class.
}

public class Service
{
public virtual int Property { get; }
}
```

## How to fix violations

To fix a violation of this rule, be sure to mock interfaces or non-sealed classes.

## 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 *"Type to mock (xxx) must be an interface, a delegate, or a non-selead, non-static class"* message.
65 changes: 65 additions & 0 deletions docs/Compilation/PosInfoMoq2010.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# PosInfoMoq2010: `Mock.Of<T>` method must be used only with types that contains parameterless contructor

| Property | Value |
|-------------------------------------|---------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2010 |
| **Title** | `Mock.Of<T>` method must be used only with types that contains parameterless contructor. |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

The `Mock.Of<T>` method must be used only with types that contains accessible parameterless constructor.

## Rule description

The `Mock.Of<T>` method must be use only for non-`sealed` classes which contains accessible parameterless constructor.

For example, the following code can not mock the `Service` class because it does not contain a parameterless constructor.

```csharp
[Fact]
public void Test()
{
var service = Mock.Of<Service>(s => s.Property == 1234); // The Service can not be mocked, because not parameterless constructor exists.
}

public class Service
{
public Service(int timeout)
{
}

public virtual int Property { get; }
}
```

In this other example, the `Service` class cannot be mocked too because it contains a private constructor.

```csharp
[Fact]
public void Test()
{
var service = Mock.Of<Service>(); // The Service can not be mocked, because the parameterless constructor is private.
}

public class Service
{
private Service()
{
}

public virtual int Property { get; }
}
```

## How to fix violations

To fix a violation of this rule, be sure to the mocked type contains an accessible parameterless constructor
(`public`, `protected` or `internal`)

## 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 not instantiate proxy of class: xxx.
Could not find a parameterless constructor. (Parameter 'constructorArguments')"* message.
46 changes: 46 additions & 0 deletions docs/Compilation/PosInfoMoq2011.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# PosInfoMoq2011: Constructor of the mocked class must be accessible.

| Property | Value |
|-------------------------------------|-------------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq2011 |
| **Title** | Constructor of the mocked class must be accessible. |
| **Category** | Compilation |
| **Default severity** | Error |

## Cause

Constructor of the mocked class must be accessible (`public`, `protected` or `internal`)

## Rule description

The mocked class must not contain an inaccessible constructor.

```csharp
[Fact]
public void Test()
{
var service1 = new Mock<Service>("Hello"); // The constructor invoked is private.
var service2 = new Mock<Service>("Argument 1", 2); // OK
var service3 = new Mock<Service>(MockBehavior.Strict, "Argument 1", 2); // OK
}

public abstract class Service
{
private Service(string a)
{
}

public Service(string a, int b)
{
}
}
```

## How to fix violations

To fix a violation of this rule, be sure to call a non-private constructor when instantiate a mocked 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 `NotSupportedException`
thrown with the *"Parent does not have a default constructor. The default constructor must be explicitly defined."* message.
Binary file added docs/Design/PosInfoMoq1001-Fixer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 26 additions & 5 deletions docs/Design/PosInfoMoq1001.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# PosInfoMoq1001: The `Mock<T>` instance behavior should be defined to Strict mode
# PosInfoMoq1001: The mocked instances behavior should be defined to `Strict` mode

| Property | Value |
|-------------------------------------|------------------------------------------------------------------|
| **Rule ID** | PosInfoMoq1001 |
| **Title** | The `Mock<T>` instance behavior should be defined to Strict mode |
| **Title** | The mocked instances behavior should be defined to `Strict` mode |
| **Category** | Design |
| **Default severity** | Warning |

## Cause

A Mock<T> instance has been created with the `Loose` behavior instead of `Strict`.
A mocked instance has been created with the `Loose` or `Default` behavior instead of `Strict`.

## Rule description

When instantiating a `Mock<T>` instance, the `MockBehavior` of the `Mock` instance should be defined to `Strict`.
When instantiating a `Mock<T>` instance (or using the `Mock.Of<T>()` alternative), the `MockBehavior` of the `Mock` instance should be defined to `Strict`.

By default, [Moq](https://github.com/devlooped/moq) build instances of mocked instance which have a `Loose` behavior for non-setup methods.

Expand Down Expand Up @@ -78,9 +78,30 @@ public void GetDataFromRepository()
}
```

The same behavior should be defined if the `Mock.Of<T>()` alternative is used:
```csharp
[Fact]
public void GetDataFromRepository()
{
// Arrange
var repository = Mock.Of<IRepository>(MockBehavior.Strict); // Strict behavior (Loose)
// /!\ No methods on the IRepository has been setup !
var service = new CustomerService(repository);

// Act
var result = service.GetDataFromRepository(); // A "MoqException" will be raised to indicate that the GetData() method has not been setup !
}
```

## How to fix violations

To fix a violation of this rule, set the `MockBehavior` to `Strict` in the constructor of the `Mock<T>` class.
To fix a violation of this rule, set the `MockBehavior` to `Strict` in the constructor of the `Mock<T>` class or as the last argument of `Mock.Of<T>()` method.

### Visual Studio fixer
A Visual Studio fixer exists to set explicitely the `MockBehavior` to `Strict` in the current document, project or solution.
![Visual Studio rule fixer](PosInfoMoq1001-Fixer.png)

## When to suppress warnings

Expand Down
11 changes: 10 additions & 1 deletion src/Moq.Analyzers/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
## Release 1.8.0
## Release 1.9.1

### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
PosInfoMoq2009 | Compilation | Error | `Mock.Of<T>` method must be used only to mock non-sealed class.
PosInfoMoq2010 | Compilation | Error | `Mock.Of<T>` method must be used only with types that contains parameterless contructor.
PosInfoMoq2011 | Compilation | Error | Constructor of the mocked class must be accessible.

## Release 1.8.0

### New Rules

Expand Down
Loading

0 comments on commit d639007

Please sign in to comment.