From 6ffd23e1bf3866dc39ab13c86ebe1f667e07398d Mon Sep 17 00:00:00 2001 From: Maciej Walkowiak Date: Tue, 11 Apr 2023 20:58:39 +0200 Subject: [PATCH] Add `ArgumentMatchers#assertArg` method (#2949) Fixes #2285 --- .../java/org/mockito/ArgumentMatchers.java | 17 +++++++ src/main/java/org/mockito/Mockito.java | 14 ++++++ .../mockitousage/matchers/MatchersTest.java | 49 +++++++++++++++++++ 3 files changed, 80 insertions(+) diff --git a/src/main/java/org/mockito/ArgumentMatchers.java b/src/main/java/org/mockito/ArgumentMatchers.java index de87f298a1..1743ce164b 100644 --- a/src/main/java/org/mockito/ArgumentMatchers.java +++ b/src/main/java/org/mockito/ArgumentMatchers.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; import java.util.regex.Pattern; import org.mockito.internal.matchers.Any; @@ -912,6 +913,22 @@ public static T argThat(ArgumentMatcher matcher) { return null; } + /** + * Allows creating custom argument matchers where matching is considered successful when the consumer given by parameter does not throw an exception. + *

+ * Typically used with {@link Mockito#verify(Object)} to execute assertions on parameters passed to the verified method invocation. + * + * @param consumer executes assertions on the verified argument + * @return null. + */ + public static T assertArg(Consumer consumer) { + return argThat( + argument -> { + consumer.accept(argument); + return true; + }); + } + /** * Allows creating custom char argument matchers. *

diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 428655e4a8..1fce63e51e 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -39,6 +39,7 @@ import org.mockito.verification.VerificationMode; import org.mockito.verification.VerificationWithTimeout; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -1661,6 +1662,19 @@ * With an implicit type, the Java compiler is unable to automatically determine the type of a mock and you need * to pass in the {@code Class} explicitly. *

+ * + *

55. + * Verification with assertions (Since 5.3.0)

+ * + * To validate arguments during verification, instead of capturing them with {@link ArgumentCaptor}, you can now + * use {@link ArgumentMatchers#assertArg(Consumer)}}: + * + *

+ *   verify(serviceMock).doStuff(assertArg(param -> {
+ *     assertThat(param.getField1()).isEqualTo("foo");
+ *     assertThat(param.getField2()).isEqualTo("bar");
+ *   }));
+ * 
*/ @CheckReturnValue @SuppressWarnings("unchecked") diff --git a/src/test/java/org/mockitousage/matchers/MatchersTest.java b/src/test/java/org/mockitousage/matchers/MatchersTest.java index 777c49a063..f89b44b731 100644 --- a/src/test/java/org/mockitousage/matchers/MatchersTest.java +++ b/src/test/java/org/mockitousage/matchers/MatchersTest.java @@ -20,6 +20,7 @@ import static org.mockito.AdditionalMatchers.lt; import static org.mockito.AdditionalMatchers.not; import static org.mockito.AdditionalMatchers.or; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; @@ -52,6 +53,7 @@ import java.util.RandomAccess; import java.util.regex.Pattern; +import org.junit.ComparisonFailure; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -624,4 +626,51 @@ public void nullable_matcher() throws Exception { verify(mock, times(2)).oneArg(nullable(Character.class)); } + + @Test + public void assertArg_matcher() throws Exception { + mock.oneArg("hello"); + + verify(mock).oneArg(assertArg((String it) -> assertEquals("hello", it))); + } + + @Test + public void assertArg_matcher_fails_when_assertion_fails() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + } + + @Test + public void can_invoke_method_on_mock_after_assert_arg() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + + mock.oneArg("hello"); + } + + @Test + public void can_verify_on_mock_after_assert_arg() throws Exception { + mock.oneArg("hello"); + + try { + verify(mock).oneArg(assertArg((String it) -> assertEquals("not-hello", it))); + fail("Should throw an exception"); + } catch (ComparisonFailure e) { + // do nothing + } + + verify(mock).oneArg("hello"); + } }