Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions Source/Mockolate/Interactions/MockInteractions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ TInteraction IMockInteractions.RegisterInteraction<TInteraction>(TInteraction in
{
_missingVerification?.Add(interaction);
_interactions.TryAdd(interaction.Index, interaction);
InteractionAdded?.Invoke(this, interaction);
return interaction;
}

Expand All @@ -46,6 +47,11 @@ public IReadOnlyCollection<IInteraction> GetUnverifiedInteractions()
return _missingVerification;
}

/// <summary>
/// Notifies whenever an interaction was registered on the mock.
/// </summary>
public event EventHandler<IInteraction>? InteractionAdded;
Comment thread
vbreuss marked this conversation as resolved.
Outdated

internal event EventHandler? OnClearing;

internal int GetNextIndex()
Expand Down
66 changes: 22 additions & 44 deletions Source/Mockolate/MockRegistration.Verify.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Mockolate.Interactions;
Expand All @@ -18,23 +17,17 @@ public VerificationResult<T> Method<T>(T subject, IMethodMatch methodMatch)
=> new(
subject,
Interactions,
Interactions.Interactions
.OfType<MethodInvocation>()
.Where(methodMatch.Matches)
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is MethodInvocation method &&
methodMatch.Matches(method),
$"invoked method {methodMatch}");

/// <summary>
/// Counts the getter accesses of property <paramref name="propertyName" /> on the <paramref name="subject" />.
/// </summary>
public VerificationResult<T> Property<T>(T subject, string propertyName) => new(subject,
Interactions,
Interactions.Interactions
.OfType<PropertyGetterAccess>()
.Where(property => property.Name.Equals(propertyName))
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is PropertyGetterAccess property &&
property.Name.Equals(propertyName),
$"got property {propertyName.SubstringAfterLast('.')}");

/// <summary>
Expand All @@ -45,12 +38,9 @@ public VerificationResult<T> Property<T>(T subject, string propertyName,
IParameter value)
=> new(subject,
Interactions,
Interactions.Interactions
.OfType<PropertySetterAccess>()
.Where(property => property.Name.Equals(propertyName) &&
value.Matches(property.Value))
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is PropertySetterAccess property &&
property.Name.Equals(propertyName) &&
value.Matches(property.Value),
$"set property {propertyName.SubstringAfterLast('.')} to value {value}");

/// <summary>
Expand All @@ -61,14 +51,11 @@ public VerificationResult<T> Indexer<T>(T subject,
params NamedParameter[] parameters)
=> new(subject,
Interactions,
Interactions.Interactions
.OfType<IndexerGetterAccess>()
.Where(indexer => indexer.Parameters.Length == parameters.Length &&
!parameters
.Where((parameter, i) => !parameter.Matches(indexer.Parameters[i]))
.Any())
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is IndexerGetterAccess indexer &&
indexer.Parameters.Length == parameters.Length &&
!parameters
.Where((parameter, i) => !parameter.Matches(indexer.Parameters[i]))
.Any(),
$"got indexer [{string.Join(", ", parameters.Select(x => x.Parameter.ToString()))}]");

/// <summary>
Expand All @@ -79,39 +66,30 @@ public VerificationResult<T> Indexer<T>(T subject, IParameter? value,
params NamedParameter[] parameters)
=> new(subject,
Interactions,
Interactions.Interactions
.OfType<IndexerSetterAccess>()
.Where(indexer => indexer.Parameters.Length == parameters.Length &&
(value?.Matches(indexer.Value) ?? indexer.Value is null) &&
!parameters
.Where((parameter, i) => !parameter.Matches(indexer.Parameters[i]))
.Any())
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is IndexerSetterAccess indexer &&
indexer.Parameters.Length == parameters.Length &&
(value?.Matches(indexer.Value) ?? indexer.Value is null) &&
!parameters
.Where((parameter, i) => !parameter.Matches(indexer.Parameters[i]))
.Any(),
$"set indexer [{string.Join(", ", parameters.Select(x => x.Parameter.ToString()))}] to value {value?.ToString() ?? "null"}");

/// <summary>
/// Counts the subscriptions to the event <paramref name="eventName" /> on the <paramref name="subject" />.
/// </summary>
public VerificationResult<T> SubscribedTo<T>(T subject, string eventName)
=> new(subject, Interactions,
Interactions.Interactions
.OfType<EventSubscription>()
.Where(@event => @event.Name.Equals(eventName))
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is EventSubscription @event &&
@event.Name.Equals(eventName),
$"subscribed to event {eventName.SubstringAfterLast('.')}");

/// <summary>
/// Counts the unsubscriptions from the event <paramref name="eventName" /> on the <paramref name="subject" />.
/// </summary>
public VerificationResult<T> UnsubscribedFrom<T>(T subject, string eventName)
=> new(subject, Interactions,
Interactions.Interactions
.OfType<EventUnsubscription>()
.Where(@event => @event.Name.Equals(eventName))
.Cast<IInteraction>()
.ToArray(),
interaction => interaction is EventUnsubscription @event &&
@event.Name.Equals(eventName),
$"unsubscribed from event {eventName.SubstringAfterLast('.')}");

/// <summary>
Expand Down
16 changes: 10 additions & 6 deletions Source/Mockolate/Verify/VerificationResult.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Mockolate.Interactions;

namespace Mockolate.Verify;
Expand All @@ -10,16 +11,18 @@ public class VerificationResult<TVerify> : IVerificationResult<TVerify>, IVerifi
{
private readonly string _expectation;
private readonly MockInteractions _interactions;
private readonly IInteraction[] _matchingInteractions;
private readonly Func<IInteraction, bool> _predicate;
private readonly TVerify _verify;

/// <inheritdoc cref="VerificationResult{TMock}" />
public VerificationResult(TVerify verify, MockInteractions interactions, IInteraction[] matchingInteractions,
public VerificationResult(TVerify verify,
MockInteractions interactions,
Func<IInteraction, bool> predicate,
string expectation)
Comment thread
vbreuss marked this conversation as resolved.
{
_verify = verify;
_interactions = interactions;
_matchingInteractions = matchingInteractions;
_predicate = predicate;
_expectation = expectation;
}

Expand All @@ -32,7 +35,7 @@ TVerify IVerificationResult<TVerify>.Object
#endregion

internal VerificationResult<T> Map<T>(T mock)
=> new(mock, _interactions, _matchingInteractions, _expectation);
=> new(mock, _interactions, _predicate, _expectation);

#region IVerificationResult

Expand All @@ -47,8 +50,9 @@ MockInteractions IVerificationResult.MockInteractions
/// <inheritdoc cref="IVerificationResult.Verify(Func{IInteraction[], Boolean})" />
bool IVerificationResult.Verify(Func<IInteraction[], bool> predicate)
{
_interactions.Verified(_matchingInteractions);
return predicate(_matchingInteractions);
IInteraction[] matchingInteractions = _interactions.Interactions.Where(_predicate).ToArray();
_interactions.Verified(matchingInteractions);
return predicate(matchingInteractions);
Comment thread
vbreuss marked this conversation as resolved.
}

#endregion
Expand Down
3 changes: 2 additions & 1 deletion Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ namespace Mockolate.Interactions
public MockInteractions() { }
public int Count { get; }
public System.Collections.Generic.IEnumerable<Mockolate.Interactions.IInteraction> Interactions { get; }
public event System.EventHandler<Mockolate.Interactions.IInteraction>? InteractionAdded;
public System.Collections.Generic.IReadOnlyCollection<Mockolate.Interactions.IInteraction> GetUnverifiedInteractions() { }
}
[System.Diagnostics.DebuggerDisplay("{ToString()}")]
Expand Down Expand Up @@ -1796,7 +1797,7 @@ namespace Mockolate.Verify
}
public class VerificationResult<TVerify> : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult<TVerify>
{
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { }
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func<Mockolate.Interactions.IInteraction, bool> predicate, string expectation) { }
}
}
namespace Mockolate.Web
Expand Down
3 changes: 2 additions & 1 deletion Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ namespace Mockolate.Interactions
public MockInteractions() { }
public int Count { get; }
public System.Collections.Generic.IEnumerable<Mockolate.Interactions.IInteraction> Interactions { get; }
public event System.EventHandler<Mockolate.Interactions.IInteraction>? InteractionAdded;
public System.Collections.Generic.IReadOnlyCollection<Mockolate.Interactions.IInteraction> GetUnverifiedInteractions() { }
}
[System.Diagnostics.DebuggerDisplay("{ToString()}")]
Expand Down Expand Up @@ -1795,7 +1796,7 @@ namespace Mockolate.Verify
}
public class VerificationResult<TVerify> : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult<TVerify>
{
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { }
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func<Mockolate.Interactions.IInteraction, bool> predicate, string expectation) { }
}
}
namespace Mockolate.Web
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ namespace Mockolate.Interactions
public MockInteractions() { }
public int Count { get; }
public System.Collections.Generic.IEnumerable<Mockolate.Interactions.IInteraction> Interactions { get; }
public event System.EventHandler<Mockolate.Interactions.IInteraction>? InteractionAdded;
public System.Collections.Generic.IReadOnlyCollection<Mockolate.Interactions.IInteraction> GetUnverifiedInteractions() { }
}
[System.Diagnostics.DebuggerDisplay("{ToString()}")]
Expand Down Expand Up @@ -1744,7 +1745,7 @@ namespace Mockolate.Verify
}
public class VerificationResult<TVerify> : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult<TVerify>
{
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { }
public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func<Mockolate.Interactions.IInteraction, bool> predicate, string expectation) { }
}
}
namespace Mockolate.Web
Expand Down
38 changes: 38 additions & 0 deletions Tests/Mockolate.Tests/Interactions/MockInteractionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using Mockolate.Interactions;

namespace Mockolate.Tests.Interactions;

public class MockInteractionsTests
{
[Fact]
public async Task InteractionAdded_ShouldIncludeInteraction()
{
List<IInteraction> addedInteractions = [];
MockInteractions sut = new();
MethodInvocation interaction = new(0, "foo", []);
sut.InteractionAdded += OnInteractionAdded;

((IMockInteractions)sut).RegisterInteraction(interaction);

sut.InteractionAdded -= OnInteractionAdded;

await That(addedInteractions).HasSingle().Which.IsSameAs(interaction);

void OnInteractionAdded(object? sender, IInteraction e)
{
addedInteractions.Add(e);
}
}

[Fact]
public async Task RegisterInteraction_ShouldRegisterInteraction()
{
MockInteractions sut = new();
MethodInvocation interaction = new(0, "foo", []);

MethodInvocation registeredInteraction = ((IMockInteractions)sut).RegisterInteraction(interaction);

await That(registeredInteraction).IsSameAs(interaction);
}
}
5 changes: 3 additions & 2 deletions Tests/Mockolate.Tests/ItTests.MatchesTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.RegularExpressions;
using Mockolate.Parameters;
using Mockolate.Verify;

namespace Mockolate.Tests;

Expand Down Expand Up @@ -58,8 +59,8 @@ public async Task AsRegex_WithTimeout_ShouldApplyTimeoutToRegex()

void Act()
{
_ = mock.VerifyMock.Invoked.DoSomethingWithString(
It.Matches("F[aeiou]+o").AsRegex(timeout: TimeSpan.FromSeconds(0)));
mock.VerifyMock.Invoked.DoSomethingWithString(
It.Matches("F[aeiou]+o").AsRegex(timeout: TimeSpan.FromSeconds(0))).AtLeastOnce();
}

await That(Act)
Expand Down
20 changes: 20 additions & 0 deletions Tests/Mockolate.Tests/Verify/MockVerifyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,24 @@ public async Task ThatAllSetupsAreUsed_WithUsedSetup_ShouldReturnTrue()

await That(sut.VerifyMock.ThatAllSetupsAreUsed()).IsTrue();
}

[Fact]
public async Task VerificationResult_ShouldUpdateWhenInteractionsChange()
{
IChocolateDispenser sut = Mock.Create<IChocolateDispenser>();

IVerificationResult result = sut.VerifyMock.Invoked.Dispense(It.Is("Dark"), It.IsAny<int>());
bool r0 = result.Verify(f => f.Length == 0);
sut.Dispense("Dark", 1);
bool r1 = result.Verify(f => f.Length == 1);
sut.Dispense("White", 2);
bool r2 = result.Verify(f => f.Length == 1);
sut.Dispense("Dark", 2);
bool r3 = result.Verify(f => f.Length == 2);

await That(r0).IsTrue().Because("No interaction was performed yet");
await That(r1).IsTrue().Because("One interaction was performed");
await That(r2).IsTrue().Because("The second interactions did not match");
await That(r3).IsTrue().Because("The third interactions did again match");
Comment thread
vbreuss marked this conversation as resolved.
Outdated
Comment thread
vbreuss marked this conversation as resolved.
Outdated
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ await That(void () => mock.VerifyMock.Invoked.Dispense(It.IsAny<string>(), It.Is
public async Task Then_WhenNoMock_ShouldThrowMockException()
{
IChocolateDispenser mock = new MyChocolateDispenser();
VerificationResult<IChocolateDispenser> result = new(mock, new MockInteractions(), [], "foo");
VerificationResult<IChocolateDispenser> result = new(mock, new MockInteractions(), _ => false, "foo");

void Act()
{
Expand Down