From 7e93f648368aedbc63fd0c89fa882a5c3a7aba65 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Wed, 14 Feb 2024 16:25:38 +0000 Subject: [PATCH 1/9] Added ability to provide filter on Arg.Do --- src/NSubstitute/Arg.cs | 17 +++++++++++++++++ src/NSubstitute/Compatibility/CompatArg.cs | 7 +++++++ .../ArgDoFromMatcher.cs | 13 +++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/NSubstitute/Arg.cs b/src/NSubstitute/Arg.cs index 458499bf..78124db0 100644 --- a/src/NSubstitute/Arg.cs +++ b/src/NSubstitute/Arg.cs @@ -125,6 +125,16 @@ public static ref T Do(Action useArgument) where T : AnyType return ref ArgumentMatcher.Enqueue(new AnyArgumentMatcher(typeof(AnyType)), x => useArgument(x!)); } + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static ref T Do(Expression> predicate, Action useArgument) + { + return ref ArgumentMatcher.Enqueue(new ExpressionArgumentMatcher(predicate), x => useArgument((T)x!)); + } + /// /// Alternate version of matchers for compatibility with pre-C#7 compilers /// which do not support ref return types. Do not use unless you are unable to use . @@ -220,6 +230,13 @@ public static class Compat /// whenever a matching call is made to the substitute. /// public static AnyType Do(Action useArgument) where T : AnyType => Arg.Do(useArgument); + + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static T Do(Expression> predicate, Action useArgument) => Arg.Do(predicate, useArgument); } private static Action InvokeDelegateAction(params object[] arguments) diff --git a/src/NSubstitute/Compatibility/CompatArg.cs b/src/NSubstitute/Compatibility/CompatArg.cs index 7d1a84f4..75a65704 100644 --- a/src/NSubstitute/Compatibility/CompatArg.cs +++ b/src/NSubstitute/Compatibility/CompatArg.cs @@ -113,4 +113,11 @@ private CompatArg() { } /// whenever a matching call is made to the substitute. /// public static Arg.AnyType Do(Action useArgument) where T : Arg.AnyType => Arg.Do(useArgument); + + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static T Do(Expression> predicate, Action useArgument) => Arg.Do(predicate, useArgument); } diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index cd49cc26..dedaf188 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -56,6 +56,19 @@ public void Should_call_action_with_each_matching_call() Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "world" })); } + [Test] + public void Should_call_action_with_each_call_matching_predicate() + { + var stringArgs = new List(); + _sub.Bar(Arg.Do(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); + + _sub.Bar("hello", 1, _someObject); + _sub.Bar("hello2", 2, _someObject); + _sub.Bar("don't use this because call doesn't match", -123, _someObject); + + Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); + } + [Test] public void Arg_do_with_when_for_any_args() { From 105be016b5c6c0d68ed1244a33e7d3a7a77acedf Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Thu, 7 Mar 2024 16:21:18 +0000 Subject: [PATCH 2/9] Run actions during Received call --- .../Routing/Handlers/CheckReceivedCallsHandler.cs | 12 ++++++++++++ .../ArgDoFromMatcher.cs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/NSubstitute/Routing/Handlers/CheckReceivedCallsHandler.cs b/src/NSubstitute/Routing/Handlers/CheckReceivedCallsHandler.cs index de7c5816..30119bed 100644 --- a/src/NSubstitute/Routing/Handlers/CheckReceivedCallsHandler.cs +++ b/src/NSubstitute/Routing/Handlers/CheckReceivedCallsHandler.cs @@ -34,6 +34,18 @@ public RouteAction Handle(ICall call) _exceptionThrower.Throw(callSpecification, matchingCalls, relatedCalls, _requiredQuantity); } + InvokePerArgumentActionsForMatchingCalls(callSpecification, matchingCalls); + return RouteAction.Continue(); } + + private static void InvokePerArgumentActionsForMatchingCalls(ICallSpecification callSpecification, List matchingCalls) + { + var callInfoFactory = new CallInfoFactory(); + + foreach (var matchingCall in matchingCalls) + { + callSpecification.InvokePerArgumentActions(callInfoFactory.Create(matchingCall)); + } + } } \ No newline at end of file diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index dedaf188..63fcf12f 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -69,6 +69,20 @@ public void Should_call_action_with_each_call_matching_predicate() Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); } + [Test] + public void Should_call_action_with_each_call_matching_predicate_assert() + { + var stringArgs = new List(); + + _sub.Bar("hello", 1, _someObject); + _sub.Bar("hello2", 2, _someObject); + _sub.Bar("don't use this because call doesn't match", -123, _someObject); + + _sub.Received(2).Bar(Arg.Do(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); + + Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); + } + [Test] public void Arg_do_with_when_for_any_args() { From c4e29b584e925d67986f5ae97cf3791d7c9b4263 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Fri, 8 Mar 2024 07:55:23 +0000 Subject: [PATCH 3/9] Moved test --- .../ArgDoFromMatcher.cs | 13 ------------ .../ReceivedCalls.cs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index 63fcf12f..eb45b5da 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -69,19 +69,6 @@ public void Should_call_action_with_each_call_matching_predicate() Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); } - [Test] - public void Should_call_action_with_each_call_matching_predicate_assert() - { - var stringArgs = new List(); - - _sub.Bar("hello", 1, _someObject); - _sub.Bar("hello2", 2, _someObject); - _sub.Bar("don't use this because call doesn't match", -123, _someObject); - - _sub.Received(2).Bar(Arg.Do(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); - - Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); - } [Test] public void Arg_do_with_when_for_any_args() diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index d7de58b4..41745658 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -1,6 +1,7 @@ using NSubstitute.Exceptions; using NSubstitute.ReceivedExtensions; using NUnit.Framework; +using System.Drawing; namespace NSubstitute.Acceptance.Specs; @@ -315,6 +316,23 @@ public void Throw_when_negative_min_range_given() StringAssert.Contains("minInclusive must be >= 0, but was -1.", ex.Message); } + [Test] + public void Should_call_action_with_each_call_matching_predicate_assert() + { + var suitCaseLuggage = new List(); + + _car.StoreLuggage(new SuitCase()); + _car.StoreLuggage(new SuitCase()); + _car.StoreLuggage(new object()); + + _car.Received(2).StoreLuggage( + Arg.Do( + x => x.All(l => l is SuitCase), + suitCaseLuggage.Add)); + + Assert.That(suitCaseLuggage, Has.Count.EqualTo(2)); + } + public interface ICar { void Start(); @@ -328,4 +346,6 @@ public interface ICar float GetCapacityInLitres(); event Action Started; } + + public class SuitCase; } \ No newline at end of file From bd8906f9a024623f6414ebaef4d184047b4639ef Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Fri, 8 Mar 2024 08:05:41 +0000 Subject: [PATCH 4/9] Renamed new method --- src/NSubstitute/Arg.cs | 34 +++++++++---------- src/NSubstitute/Compatibility/CompatArg.cs | 14 ++++---- .../ArgDoFromMatcher.cs | 2 +- .../ReceivedCalls.cs | 2 +- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/NSubstitute/Arg.cs b/src/NSubstitute/Arg.cs index 78124db0..1277a679 100644 --- a/src/NSubstitute/Arg.cs +++ b/src/NSubstitute/Arg.cs @@ -58,6 +58,16 @@ public static ref T Is(Expression> predicate) where T : Any return ref ArgumentMatcher.Enqueue(new ExpressionArgumentMatcher(predicate)); } + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is or was made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static ref T Is(Expression> predicate, Action useArgument) + { + return ref ArgumentMatcher.Enqueue(new ExpressionArgumentMatcher(predicate), x => useArgument((T)x!)); + } + /// /// Invoke any argument whenever a matching call is made to the substitute. /// @@ -125,16 +135,6 @@ public static ref T Do(Action useArgument) where T : AnyType return ref ArgumentMatcher.Enqueue(new AnyArgumentMatcher(typeof(AnyType)), x => useArgument(x!)); } - /// - /// Match argument that satisfies and use it to call the function - /// whenever a matching call is made to the substitute. - /// If the throws an exception for an argument it will be treated as non-matching. - /// - public static ref T Do(Expression> predicate, Action useArgument) - { - return ref ArgumentMatcher.Enqueue(new ExpressionArgumentMatcher(predicate), x => useArgument((T)x!)); - } - /// /// Alternate version of matchers for compatibility with pre-C#7 compilers /// which do not support ref return types. Do not use unless you are unable to use . @@ -174,6 +174,13 @@ public static class Compat /// public static AnyType Is(Expression> predicate) where T : AnyType => Arg.Is(predicate); + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static T Is(Expression> predicate, Action useArgument) => Arg.Is(predicate, useArgument); + /// /// Invoke any argument whenever a matching call is made to the substitute. /// This is provided for compatibility with older compilers -- @@ -230,13 +237,6 @@ public static class Compat /// whenever a matching call is made to the substitute. /// public static AnyType Do(Action useArgument) where T : AnyType => Arg.Do(useArgument); - - /// - /// Match argument that satisfies and use it to call the function - /// whenever a matching call is made to the substitute. - /// If the throws an exception for an argument it will be treated as non-matching. - /// - public static T Do(Expression> predicate, Action useArgument) => Arg.Do(predicate, useArgument); } private static Action InvokeDelegateAction(params object[] arguments) diff --git a/src/NSubstitute/Compatibility/CompatArg.cs b/src/NSubstitute/Compatibility/CompatArg.cs index 75a65704..daa53329 100644 --- a/src/NSubstitute/Compatibility/CompatArg.cs +++ b/src/NSubstitute/Compatibility/CompatArg.cs @@ -49,6 +49,13 @@ private CompatArg() { } /// public T Is(Expression> predicate) => Arg.Is(predicate); + /// + /// Match argument that satisfies and use it to call the function + /// whenever a matching call is or was made to the substitute. + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static T Is(Expression> predicate, Action useArgument) => Arg.Is(predicate, useArgument); + /// /// Match argument that satisfies . /// If the throws an exception for an argument it will be treated as non-matching. @@ -113,11 +120,4 @@ private CompatArg() { } /// whenever a matching call is made to the substitute. /// public static Arg.AnyType Do(Action useArgument) where T : Arg.AnyType => Arg.Do(useArgument); - - /// - /// Match argument that satisfies and use it to call the function - /// whenever a matching call is made to the substitute. - /// If the throws an exception for an argument it will be treated as non-matching. - /// - public static T Do(Expression> predicate, Action useArgument) => Arg.Do(predicate, useArgument); } diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index eb45b5da..b1b5bbaa 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -60,7 +60,7 @@ public void Should_call_action_with_each_matching_call() public void Should_call_action_with_each_call_matching_predicate() { var stringArgs = new List(); - _sub.Bar(Arg.Do(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); + _sub.Bar(Arg.Is(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); _sub.Bar("hello", 1, _someObject); _sub.Bar("hello2", 2, _someObject); diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index 41745658..d0cfaedc 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -326,7 +326,7 @@ public void Should_call_action_with_each_call_matching_predicate_assert() _car.StoreLuggage(new object()); _car.Received(2).StoreLuggage( - Arg.Do( + Arg.Is( x => x.All(l => l is SuitCase), suitCaseLuggage.Add)); From 80e38c57efb618a12e65744e2019add5798e1e4d Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Fri, 8 Mar 2024 08:07:11 +0000 Subject: [PATCH 5/9] Removed unused using --- tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index d0cfaedc..23718fec 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -1,7 +1,6 @@ using NSubstitute.Exceptions; using NSubstitute.ReceivedExtensions; using NUnit.Framework; -using System.Drawing; namespace NSubstitute.Acceptance.Specs; From 4715cbfbf2b66ed35accf8526f636caa56514520 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Fri, 8 Mar 2024 08:53:18 +0000 Subject: [PATCH 6/9] Renamed test --- tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index 23718fec..0475d1bd 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -316,7 +316,7 @@ public void Throw_when_negative_min_range_given() } [Test] - public void Should_call_action_with_each_call_matching_predicate_assert() + public void Should_call_action_for_each_call_matching_predicate() { var suitCaseLuggage = new List(); From da66a835540d080f4527fef94478120d6f362cca Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Fri, 8 Mar 2024 14:58:27 +0000 Subject: [PATCH 7/9] Remove unnecessary whitespace --- tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index b1b5bbaa..1c71db75 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -69,7 +69,6 @@ public void Should_call_action_with_each_call_matching_predicate() Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); } - [Test] public void Arg_do_with_when_for_any_args() { From 8c8a37a1860dca2b97564b5558039c778b3aad06 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Mon, 22 Apr 2024 09:21:35 +0100 Subject: [PATCH 8/9] Added Match. Renamed Arg.Is to IsAndDo --- src/NSubstitute/Arg.cs | 4 +- src/NSubstitute/Compatibility/CompatArg.cs | 2 +- src/NSubstitute/Match.cs | 43 +++++++++++++++++++ .../ArgDoFromMatcher.cs | 15 ++++++- .../ReceivedCalls.cs | 21 ++++++++- 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 src/NSubstitute/Match.cs diff --git a/src/NSubstitute/Arg.cs b/src/NSubstitute/Arg.cs index 1277a679..2ff1e931 100644 --- a/src/NSubstitute/Arg.cs +++ b/src/NSubstitute/Arg.cs @@ -63,7 +63,7 @@ public static ref T Is(Expression> predicate) where T : Any /// whenever a matching call is or was made to the substitute. /// If the throws an exception for an argument it will be treated as non-matching. /// - public static ref T Is(Expression> predicate, Action useArgument) + public static ref T IsAndDo(Expression> predicate, Action useArgument) { return ref ArgumentMatcher.Enqueue(new ExpressionArgumentMatcher(predicate), x => useArgument((T)x!)); } @@ -179,7 +179,7 @@ public static class Compat /// whenever a matching call is made to the substitute. /// If the throws an exception for an argument it will be treated as non-matching. /// - public static T Is(Expression> predicate, Action useArgument) => Arg.Is(predicate, useArgument); + public static T IsAndDo(Expression> predicate, Action useArgument) => Arg.IsAndDo(predicate, useArgument); /// /// Invoke any argument whenever a matching call is made to the substitute. diff --git a/src/NSubstitute/Compatibility/CompatArg.cs b/src/NSubstitute/Compatibility/CompatArg.cs index daa53329..10f14772 100644 --- a/src/NSubstitute/Compatibility/CompatArg.cs +++ b/src/NSubstitute/Compatibility/CompatArg.cs @@ -54,7 +54,7 @@ private CompatArg() { } /// whenever a matching call is or was made to the substitute. /// If the throws an exception for an argument it will be treated as non-matching. /// - public static T Is(Expression> predicate, Action useArgument) => Arg.Is(predicate, useArgument); + public static T IsAndDo(Expression> predicate, Action useArgument) => Arg.IsAndDo(predicate, useArgument); /// /// Match argument that satisfies . diff --git a/src/NSubstitute/Match.cs b/src/NSubstitute/Match.cs new file mode 100644 index 00000000..d161dcfe --- /dev/null +++ b/src/NSubstitute/Match.cs @@ -0,0 +1,43 @@ +using NSubstitute.Core.Arguments; +using System.Linq.Expressions; + +namespace NSubstitute; + +/// +/// Argument matcher allowing a match predicate and optional action to be called for each match to be specified serparately. +/// +public class Match +{ + private Expression> predicate; + private Action useArgument; + + internal Match(Expression> predicate, Action useArgument) + { + this.predicate = predicate; + this.useArgument = useArgument; + } + + /// + /// The function to be invoked + /// for each matching call made to the substitute. + /// + public Match AndDo(Action useArgument) => new Match(predicate, x => { this.useArgument(x); useArgument(x); }); + + public static implicit operator T? (Match match) + { + return ArgumentMatcher.Enqueue( + new ExpressionArgumentMatcher(match.predicate), + x => match.useArgument((T?)x) + ); + } +} + +public static class Match +{ + /// + /// Match argument that satisfies . + /// If the throws an exception for an argument it will be treated as non-matching. + /// + public static Match When(Expression> predicate) => + new Match(predicate, x => { }); +} diff --git a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs index 1c71db75..5c5be66a 100644 --- a/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs +++ b/tests/NSubstitute.Acceptance.Specs/ArgDoFromMatcher.cs @@ -56,11 +56,24 @@ public void Should_call_action_with_each_matching_call() Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "world" })); } + [Test] + public void Should_call_action_with_each_call_matching_predicate_using_isanddo() + { + var stringArgs = new List(); + _sub.Bar(Arg.IsAndDo(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); + + _sub.Bar("hello", 1, _someObject); + _sub.Bar("hello2", 2, _someObject); + _sub.Bar("don't use this because call doesn't match", -123, _someObject); + + Assert.That(stringArgs, Is.EqualTo(new[] { "hello", "hello2" })); + } + [Test] public void Should_call_action_with_each_call_matching_predicate() { var stringArgs = new List(); - _sub.Bar(Arg.Is(x => x.StartsWith("h"), x => stringArgs.Add(x)), Arg.Any(), _someObject); + _sub.Bar(Match.When(x => x.StartsWith("h")).AndDo(stringArgs.Add), Arg.Any(), _someObject); _sub.Bar("hello", 1, _someObject); _sub.Bar("hello2", 2, _someObject); diff --git a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs index 0475d1bd..6ceb0522 100644 --- a/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs +++ b/tests/NSubstitute.Acceptance.Specs/ReceivedCalls.cs @@ -316,7 +316,7 @@ public void Throw_when_negative_min_range_given() } [Test] - public void Should_call_action_for_each_call_matching_predicate() + public void Should_call_action_for_each_call_matching_predicate_using_isanddo() { var suitCaseLuggage = new List(); @@ -325,13 +325,30 @@ public void Should_call_action_for_each_call_matching_predicate() _car.StoreLuggage(new object()); _car.Received(2).StoreLuggage( - Arg.Is( + Arg.IsAndDo( x => x.All(l => l is SuitCase), suitCaseLuggage.Add)); Assert.That(suitCaseLuggage, Has.Count.EqualTo(2)); } + [Test] + public void Should_call_action_for_each_call_matching_predicate() + { + var suitCaseLuggage = new List(); + + _car.StoreLuggage(new SuitCase()); + _car.StoreLuggage(new SuitCase()); + _car.StoreLuggage(new object()); + + _car.Received(2).StoreLuggage( + Match.When( + x => x.All(l => l is SuitCase)) + .AndDo(suitCaseLuggage.Add)); + + Assert.That(suitCaseLuggage, Has.Count.EqualTo(2)); + } + public interface ICar { void Start(); From 10b6bc08499a66e68a12a5e51f92d1884a5dcc51 Mon Sep 17 00:00:00 2001 From: Adam Cox Date: Mon, 22 Apr 2024 14:49:13 +0100 Subject: [PATCH 9/9] Fix typo --- src/NSubstitute/Match.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NSubstitute/Match.cs b/src/NSubstitute/Match.cs index d161dcfe..8d6c7ea5 100644 --- a/src/NSubstitute/Match.cs +++ b/src/NSubstitute/Match.cs @@ -4,7 +4,7 @@ namespace NSubstitute; /// -/// Argument matcher allowing a match predicate and optional action to be called for each match to be specified serparately. +/// Argument matcher allowing a match predicate and optional action to be called for each match to be specified separately. /// public class Match {