Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions Docs/pages/advanced-features/03-monitor-interactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ can use a `MockMonitor<T>`:

```csharp
var sut = Mock.Create<IChocolateDispenser>();
var monitor = new MockMonitor<IChocolateDispenser>(sut);

sut.Dispense("Dark", 1); // Not monitored
var monitorScope = sut.MonitorMock(out var monitor);
using (monitorScope)
using (monitor.Run())
{
sut.Dispense("Dark", 2); // Monitored
}
Expand All @@ -18,6 +18,21 @@ sut.Dispense("Dark", 3); // Not monitored
monitor.Verify.Invoked.Dispense(It.Is("Dark"), It.IsAny<int>()).Once();
```

Alternatively, you can use the `MonitorMock()` extension method to create an already running monitor directly from the
mock:

```csharp
var sut = Mock.Create<IChocolateDispenser>();

sut.Dispense("Dark", 1); // Not monitored
using var scope = sut.MonitorMock(out var monitor);
sut.Dispense("Dark", 2); // Monitored
sut.Dispense("Dark", 3); // Monitored

// Verifications on the monitor only count interactions during the lifetime scope of the `IDisposable`
monitor.Verify.Invoked.Dispense(It.Is("Dark"), It.IsAny<int>()).Twice();
```

## Clear all interactions

For simpler scenarios you can directly clear all recorded interactions on a mock using `ClearAllInteractions` on the
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -743,10 +743,10 @@ can use a `MockMonitor<T>`:

```csharp
var sut = Mock.Create<IChocolateDispenser>();
var monitor = new MockMonitor<IChocolateDispenser>(sut);

sut.Dispense("Dark", 1); // Not monitored
var monitorScope = sut.MonitorMock(out var monitor);
using (monitorScope)
using (monitor.Run())
{
sut.Dispense("Dark", 2); // Monitored
}
Expand All @@ -756,6 +756,21 @@ sut.Dispense("Dark", 3); // Not monitored
monitor.Verify.Invoked.Dispense(It.Is("Dark"), It.IsAny<int>()).Once();
```

Alternatively, you can use the `MonitorMock()` extension method to create an already running monitor directly from the
mock:

```csharp
var sut = Mock.Create<IChocolateDispenser>();

sut.Dispense("Dark", 1); // Not monitored
using var scope = sut.MonitorMock(out var monitor);
sut.Dispense("Dark", 2); // Monitored
sut.Dispense("Dark", 3); // Monitored

// Verifications on the monitor only count interactions during the lifetime scope of the `IDisposable`
monitor.Verify.Invoked.Dispense(It.Is("Dark"), It.IsAny<int>()).Twice();
```

#### Clear all interactions

For simpler scenarios you can directly clear all recorded interactions on a mock using `ClearAllInteractions` on the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ namespace Mockolate;
.Append(@class.ClassFullName.EscapeForXmlDoc()).Append("\" />.").AppendLine();
sb.Append("\t\t/// </summary>").AppendLine();
sb.Append("\t\tpublic System.IDisposable MonitorMock(out MockMonitor<").Append(@class.ClassFullName)
.AppendLine("> monitor)").AppendLine();
.Append("> monitor)").AppendLine();
sb.Append("\t\t{").AppendLine();
sb.Append("\t\t\tmonitor = new MockMonitor<").Append(@class.ClassFullName)
.AppendLine(">(GetMockOrThrow(subject));").AppendLine();
.AppendLine(">(subject);").AppendLine();
sb.Append("\t\t\treturn monitor.Run();").AppendLine();
sb.Append("\t\t}").AppendLine();
sb.AppendLine("\t}");
Expand Down Expand Up @@ -203,10 +203,10 @@ private static Mock<T> GetMockOrThrow<T>(T subject) where T : System.Delegate
.Append(@class.ClassFullName.EscapeForXmlDoc()).Append("\" />.").AppendLine();
sb.Append("\t\t/// </summary>").AppendLine();
sb.Append("\t\tpublic System.IDisposable MonitorMock(out MockMonitor<").Append(@class.ClassFullName)
.AppendLine("> monitor)").AppendLine();
.Append("> monitor)").AppendLine();
sb.Append("\t\t{").AppendLine();
sb.Append("\t\t\tmonitor = new MockMonitor<").Append(@class.ClassFullName)
.AppendLine(">(GetMockOrThrow(subject));").AppendLine();
.AppendLine(">(subject);").AppendLine();
sb.Append("\t\t\treturn monitor.Run();").AppendLine();
sb.Append("\t\t}").AppendLine();
sb.AppendLine("\t}");
Expand Down
17 changes: 12 additions & 5 deletions Source/Mockolate/Monitor/MockMonitor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Mockolate.Exceptions;
using Mockolate.Interactions;
using Mockolate.Verify;

Expand Down Expand Up @@ -100,16 +101,22 @@ private sealed class MonitorScope(Action callback) : IDisposable
/// </summary>
/// <remarks>
/// Use this class to track and analyze interactions with a mock, such as which members were accessed or
/// which events were subscribed to, during a test session. Monitoring is session-based; begin a session with the Run
/// method and dispose the returned scope to finalize monitoring.
/// which events were subscribed to, during a test session.<br />
/// Monitoring is session-based: begin a session with the <see cref="MockMonitor.Run()" /> method and
/// dispose the returned scope to finalize monitoring.
/// </remarks>
public sealed class MockMonitor<T> : MockMonitor
{
/// <inheritdoc cref="MockMonitor{T}" />
public MockMonitor(Mock<T> mock) : base(mock.Interactions)
public MockMonitor(T mock) : this(mock as IMockSubject<T>)
{
}

private MockMonitor(IMockSubject<T>? mockSubject)
: base(mockSubject?.Mock.Interactions ?? throw new MockException("The subject is no mock."))
{
Verify = new Mock<T>(mock.Subject,
new MockRegistration(mock.Registrations.Behavior, mock.Registrations.Prefix, Interactions));
Verify = new Mock<T>(mockSubject.Mock.Subject,
new MockRegistration(mockSubject.Registrations.Behavior, mockSubject.Registrations.Prefix, Interactions));
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ namespace Mockolate.Monitor
}
public sealed class MockMonitor<T> : Mockolate.Monitor.MockMonitor
{
public MockMonitor(Mockolate.Mock<T> mock) { }
public MockMonitor(T mock) { }
public Mockolate.Verify.IMockVerify<T> Verify { get; }
}
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ namespace Mockolate.Monitor
}
public sealed class MockMonitor<T> : Mockolate.Monitor.MockMonitor
{
public MockMonitor(Mockolate.Mock<T> mock) { }
public MockMonitor(T mock) { }
public Mockolate.Verify.IMockVerify<T> Verify { get; }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ namespace Mockolate.Monitor
}
public sealed class MockMonitor<T> : Mockolate.Monitor.MockMonitor
{
public MockMonitor(Mockolate.Mock<T> mock) { }
public MockMonitor(T mock) { }
public Mockolate.Verify.IMockVerify<T> Verify { get; }
}
}
Expand Down
36 changes: 22 additions & 14 deletions Tests/Mockolate.Tests/Monitor/MockMonitorTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Mockolate.Exceptions;
using Mockolate.Monitor;
using Mockolate.Tests.TestHelpers;

Expand All @@ -9,8 +10,7 @@ public sealed class MockMonitorTests
public async Task ClearAllInteractions_WhenMonitorIsRunning_ShouldClearInternalCollection()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

sut.IsValid(1);
using IDisposable disposable = monitor.Run();
Expand All @@ -20,12 +20,25 @@ public async Task ClearAllInteractions_WhenMonitorIsRunning_ShouldClearInternalC
await That(monitor.Verify.Invoked.IsValid(It.Is(1))).Never();
}

[Fact]
public async Task Constructor_WithoutMock_ShouldThrowMockException()
{
MyServiceBase sut = new();

void Act()
{
_ = new MockMonitor<MyServiceBase>(sut);
}

await That(Act).Throws<MockException>()
.WithMessage("The subject is no mock.");
}

[Fact]
public async Task DisposeTwice_ShouldNotIncludeMoreInvocations()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

sut.IsValid(1);
sut.IsValid(2);
Expand Down Expand Up @@ -53,8 +66,7 @@ public async Task DisposeTwice_ShouldNotIncludeMoreInvocations()
public async Task MultipleRun_ShouldMonitorInvocationsDuringTheRun()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

sut.IsValid(1);
sut.IsValid(2);
Expand Down Expand Up @@ -91,8 +103,7 @@ public async Task MultipleRun_ShouldMonitorInvocationsDuringTheRun()
public async Task NestedRun_ShouldThrowInvalidOperationException()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

void Act()
{
Expand All @@ -113,8 +124,7 @@ await That(Act).Throws<InvalidOperationException>()
public async Task Run_ShouldIncludeAllInvocations()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

using (monitor.Run())
{
Expand All @@ -134,8 +144,7 @@ public async Task Run_ShouldIncludeAllInvocations()
public async Task Run_ShouldMonitorInvocationsDuringTheRun()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

sut.IsValid(1);
sut.IsValid(2);
Expand Down Expand Up @@ -163,8 +172,7 @@ public async Task Run_ShouldMonitorInvocationsDuringTheRun()
public async Task Verify_WhileRunning_ShouldBeUpToDate()
{
IMyService sut = Mock.Create<IMyService>();
Mock<IMyService> mock = ((IMockSubject<IMyService>)sut).Mock;
MockMonitor<IMyService> monitor = new(mock);
MockMonitor<IMyService> monitor = new(sut);

using (monitor.Run())
{
Expand Down
Loading