diff --git a/Source/Mockolate/Interactions/MockInteractions.cs b/Source/Mockolate/Interactions/MockInteractions.cs index 37079931..ff0475ff 100644 --- a/Source/Mockolate/Interactions/MockInteractions.cs +++ b/Source/Mockolate/Interactions/MockInteractions.cs @@ -34,6 +34,7 @@ TInteraction IMockInteractions.RegisterInteraction(TInteraction in { _missingVerification?.Add(interaction); _interactions.TryAdd(interaction.Index, interaction); + InteractionAdded?.Invoke(this, EventArgs.Empty); return interaction; } @@ -46,6 +47,7 @@ public IReadOnlyCollection GetUnverifiedInteractions() return _missingVerification; } + internal event EventHandler? InteractionAdded; internal event EventHandler? OnClearing; internal int GetNextIndex() diff --git a/Source/Mockolate/MockRegistration.Verify.cs b/Source/Mockolate/MockRegistration.Verify.cs index f5e9dce7..8c080f40 100644 --- a/Source/Mockolate/MockRegistration.Verify.cs +++ b/Source/Mockolate/MockRegistration.Verify.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using Mockolate.Interactions; @@ -18,11 +17,8 @@ public VerificationResult Method(T subject, IMethodMatch methodMatch) => new( subject, Interactions, - Interactions.Interactions - .OfType() - .Where(methodMatch.Matches) - .Cast() - .ToArray(), + interaction => interaction is MethodInvocation method && + methodMatch.Matches(method), $"invoked method {methodMatch}"); /// @@ -30,11 +26,8 @@ public VerificationResult Method(T subject, IMethodMatch methodMatch) /// public VerificationResult Property(T subject, string propertyName) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .Where(property => property.Name.Equals(propertyName)) - .Cast() - .ToArray(), + interaction => interaction is PropertyGetterAccess property && + property.Name.Equals(propertyName), $"got property {propertyName.SubstringAfterLast('.')}"); /// @@ -45,12 +38,9 @@ public VerificationResult Property(T subject, string propertyName, IParameter value) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .Where(property => property.Name.Equals(propertyName) && - value.Matches(property.Value)) - .Cast() - .ToArray(), + interaction => interaction is PropertySetterAccess property && + property.Name.Equals(propertyName) && + value.Matches(property.Value), $"set property {propertyName.SubstringAfterLast('.')} to value {value}"); /// @@ -61,14 +51,11 @@ public VerificationResult Indexer(T subject, params NamedParameter[] parameters) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .Where(indexer => indexer.Parameters.Length == parameters.Length && - !parameters - .Where((parameter, i) => !parameter.Matches(indexer.Parameters[i])) - .Any()) - .Cast() - .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()))}]"); /// @@ -79,15 +66,12 @@ public VerificationResult Indexer(T subject, IParameter? value, params NamedParameter[] parameters) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .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() - .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"}"); /// @@ -95,11 +79,8 @@ public VerificationResult Indexer(T subject, IParameter? value, /// public VerificationResult SubscribedTo(T subject, string eventName) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .Where(@event => @event.Name.Equals(eventName)) - .Cast() - .ToArray(), + interaction => interaction is EventSubscription @event && + @event.Name.Equals(eventName), $"subscribed to event {eventName.SubstringAfterLast('.')}"); /// @@ -107,11 +88,8 @@ public VerificationResult SubscribedTo(T subject, string eventName) /// public VerificationResult UnsubscribedFrom(T subject, string eventName) => new(subject, Interactions, - Interactions.Interactions - .OfType() - .Where(@event => @event.Name.Equals(eventName)) - .Cast() - .ToArray(), + interaction => interaction is EventUnsubscription @event && + @event.Name.Equals(eventName), $"unsubscribed from event {eventName.SubstringAfterLast('.')}"); /// diff --git a/Source/Mockolate/Verify/VerificationResult.cs b/Source/Mockolate/Verify/VerificationResult.cs index e2967aaa..676d2ec8 100644 --- a/Source/Mockolate/Verify/VerificationResult.cs +++ b/Source/Mockolate/Verify/VerificationResult.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Mockolate.Interactions; namespace Mockolate.Verify; @@ -10,16 +11,18 @@ public class VerificationResult : IVerificationResult, IVerifi { private readonly string _expectation; private readonly MockInteractions _interactions; - private readonly IInteraction[] _matchingInteractions; + private readonly Func _predicate; private readonly TVerify _verify; /// - public VerificationResult(TVerify verify, MockInteractions interactions, IInteraction[] matchingInteractions, + public VerificationResult(TVerify verify, + MockInteractions interactions, + Func predicate, string expectation) { _verify = verify; _interactions = interactions; - _matchingInteractions = matchingInteractions; + _predicate = predicate; _expectation = expectation; } @@ -32,7 +35,7 @@ TVerify IVerificationResult.Object #endregion internal VerificationResult Map(T mock) - => new(mock, _interactions, _matchingInteractions, _expectation); + => new(mock, _interactions, _predicate, _expectation); #region IVerificationResult @@ -47,8 +50,9 @@ MockInteractions IVerificationResult.MockInteractions /// bool IVerificationResult.Verify(Func predicate) { - _interactions.Verified(_matchingInteractions); - return predicate(_matchingInteractions); + IInteraction[] matchingInteractions = _interactions.Interactions.Where(_predicate).ToArray(); + _interactions.Verified(matchingInteractions); + return predicate(matchingInteractions); } #endregion diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt index 7edfbf5d..5b8cc9fd 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net10.0.txt @@ -1796,7 +1796,7 @@ namespace Mockolate.Verify } public class VerificationResult : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult { - public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { } + public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func predicate, string expectation) { } } } namespace Mockolate.Web diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt index 8199b859..a8f4c048 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_net8.0.txt @@ -1795,7 +1795,7 @@ namespace Mockolate.Verify } public class VerificationResult : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult { - public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { } + public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func predicate, string expectation) { } } } namespace Mockolate.Web diff --git a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt index 715d3b1c..6e79b660 100644 --- a/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt +++ b/Tests/Mockolate.Api.Tests/Expected/Mockolate_netstandard2.0.txt @@ -1744,7 +1744,7 @@ namespace Mockolate.Verify } public class VerificationResult : Mockolate.Verify.IVerificationResult, Mockolate.Verify.IVerificationResult { - public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, Mockolate.Interactions.IInteraction[] matchingInteractions, string expectation) { } + public VerificationResult(TVerify verify, Mockolate.Interactions.MockInteractions interactions, System.Func predicate, string expectation) { } } } namespace Mockolate.Web diff --git a/Tests/Mockolate.Internal.Tests/MockInteractionsTests.cs b/Tests/Mockolate.Internal.Tests/MockInteractionsTests.cs new file mode 100644 index 00000000..136c7479 --- /dev/null +++ b/Tests/Mockolate.Internal.Tests/MockInteractionsTests.cs @@ -0,0 +1,37 @@ +using Mockolate.Interactions; + +namespace Mockolate.Internal.Tests; + +public class MockInteractionsTests +{ + [Fact] + public async Task InteractionAdded_ShouldIncludeInteraction() + { + int interactionCount = 0; + MockInteractions sut = new(); + MethodInvocation interaction = new(0, "foo", []); + sut.InteractionAdded += OnInteractionAdded; + + ((IMockInteractions)sut).RegisterInteraction(interaction); + + sut.InteractionAdded -= OnInteractionAdded; + + await That(interactionCount).IsEqualTo(1); + + void OnInteractionAdded(object? sender, EventArgs e) + { + interactionCount++; + } + } + + [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); + } +} diff --git a/Tests/Mockolate.Tests/ItTests.MatchesTests.cs b/Tests/Mockolate.Tests/ItTests.MatchesTests.cs index 41e479da..82205d1e 100644 --- a/Tests/Mockolate.Tests/ItTests.MatchesTests.cs +++ b/Tests/Mockolate.Tests/ItTests.MatchesTests.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using Mockolate.Parameters; +using Mockolate.Verify; namespace Mockolate.Tests; @@ -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) diff --git a/Tests/Mockolate.Tests/Verify/MockVerifyTests.cs b/Tests/Mockolate.Tests/Verify/MockVerifyTests.cs index 93a1b272..12b73a8f 100644 --- a/Tests/Mockolate.Tests/Verify/MockVerifyTests.cs +++ b/Tests/Mockolate.Tests/Verify/MockVerifyTests.cs @@ -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(); + + IVerificationResult result = sut.VerifyMock.Invoked.Dispense(It.Is("Dark"), It.IsAny()); + 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 interaction did not match"); + await That(r3).IsTrue().Because("The third interaction did again match"); + } } diff --git a/Tests/Mockolate.Tests/Verify/VerificationResultExtensionsTests.cs b/Tests/Mockolate.Tests/Verify/VerificationResultExtensionsTests.cs index 4323fdde..21dfbcfd 100644 --- a/Tests/Mockolate.Tests/Verify/VerificationResultExtensionsTests.cs +++ b/Tests/Mockolate.Tests/Verify/VerificationResultExtensionsTests.cs @@ -347,7 +347,7 @@ await That(void () => mock.VerifyMock.Invoked.Dispense(It.IsAny(), It.Is public async Task Then_WhenNoMock_ShouldThrowMockException() { IChocolateDispenser mock = new MyChocolateDispenser(); - VerificationResult result = new(mock, new MockInteractions(), [], "foo"); + VerificationResult result = new(mock, new MockInteractions(), _ => false, "foo"); void Act() {