|
| 1 | +# PosInfoMoq2017: `Mock<T>.Raise()`/`RaiseAsync()` must use parameters matching the event signature |
| 2 | + |
| 3 | +| Property | Value | |
| 4 | +|-----------------------|----------------------------------------------------------------------| |
| 5 | +| **Rule ID** | PosInfoMoq2017 | |
| 6 | +| **Title** | `Mock<T>.Raise()`/`RaiseAsync()` must use parameters matching the event signature | |
| 7 | +| **Category** | Compilation | |
| 8 | +| **Default severity** | Error | |
| 9 | + |
| 10 | +## Cause |
| 11 | + |
| 12 | +The parameters passed to `Raise()` or `RaiseAsync()` must exactly match the parameters of the corresponding event delegate. |
| 13 | + |
| 14 | +## Rule description |
| 15 | + |
| 16 | +When mocking events with **Moq**, calling `Mock<T>.Raise()` or `Mock<T>.RaiseAsync()` requires that the provided arguments match the event signature. |
| 17 | + |
| 18 | +- With the **`Raise(event, EventArgs)`** overload: |
| 19 | + - Used for `EventHandler` or `EventHandler<T>`. |
| 20 | + - Moq automatically supplies the `sender` (the mocked object). |
| 21 | + - You only pass the `EventArgs` (or derived class) instance. |
| 22 | + |
| 23 | +- With the **`Raise(event, params object[])`** overload: |
| 24 | + - You provide all the arguments of the event delegate yourself. |
| 25 | + - This includes the `sender` and all additional event arguments. |
| 26 | + |
| 27 | +### Example |
| 28 | + |
| 29 | +```csharp |
| 30 | +public class Service |
| 31 | +{ |
| 32 | + public event EventHandler Changed; |
| 33 | + public event EventHandler<DataEventArgs> DataChanged; |
| 34 | + |
| 35 | + public void DoSomething() |
| 36 | + { |
| 37 | + this.Changed?.Invoke(this, EventArgs.Empty); |
| 38 | + this.DataChanged?.Invoke(this, new DataEventArgs(42)); |
| 39 | + } |
| 40 | +} |
| 41 | + |
| 42 | +public class DataEventArgs : EventArgs |
| 43 | +{ |
| 44 | + public int Value { get; } |
| 45 | + public DataEventArgs(int value) => Value = value; |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +#### Correct usage (matching parameters) |
| 50 | + |
| 51 | +```csharp |
| 52 | +var serviceMock = new Mock<Service>(); |
| 53 | + |
| 54 | +// Raise with EventHandler (sender is mocked object, EventArgs required) |
| 55 | +serviceMock.Raise(s => s.Changed += null, EventArgs.Empty); |
| 56 | + |
| 57 | +// Raise with EventHandler<T> (sender is mocked object, T required) |
| 58 | +serviceMock.Raise(s => s.DataChanged += null, new DataEventArgs(42)); |
| 59 | + |
| 60 | +// Raise with params object[] (sender + event args explicitly) |
| 61 | +serviceMock.Raise(s => s.DataChanged += null, "CustomSender", new DataEventArgs(42)); |
| 62 | +``` |
| 63 | + |
| 64 | +#### Incorrect usage (parameters not matching) |
| 65 | + |
| 66 | +```csharp |
| 67 | +var serviceMock = new Mock<Service>(); |
| 68 | + |
| 69 | +// Missing EventArgs |
| 70 | +serviceMock.Raise(s => s.Changed += null); // ❌ |
| 71 | +
|
| 72 | +// Wrong argument type |
| 73 | +serviceMock.Raise(s => s.DataChanged += null, EventArgs.Empty); // ❌ |
| 74 | +``` |
| 75 | + |
| 76 | +## How to fix violations |
| 77 | + |
| 78 | +To fix a violation, ensure that the arguments passed to `Raise()` or `RaiseAsync()` match **exactly** the event delegate signature: |
| 79 | + |
| 80 | +- `EventHandler`: `(object sender, EventArgs e)` |
| 81 | +- `EventHandler<T>`: `(object sender, T e)` |
| 82 | + |
| 83 | +Depending on the overload: |
| 84 | +- With `Raise(event, EventArgs)` → provide only `EventArgs` or `T`. |
| 85 | +- With `Raise(event, params object[])` → provide both `sender` and `EventArgs` (or `T`). |
| 86 | + |
| 87 | +## When to suppress warnings |
| 88 | + |
| 89 | +Do not suppress errors from this rule. If skipped, Moq will throw a runtime exception such as `TargetParameterCountException` |
| 90 | +(with the following message *"Parameter count mismatch"*) or an `ArgumentException` to explain that a value can not be converted |
| 91 | +to an other type. |
0 commit comments