From ac2c9f3d3629e07b02d88e237db5db49e12e5b1e Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Fri, 28 Oct 2022 18:49:16 +0300 Subject: [PATCH 1/6] automatically detect class to mock this commit adds method `Mockito.mock()` as a shorter alternative for `Mockito.mock(Class)`. When result of this call is assigned to a variable/field, java will detect the needed class automatically. P.S. I had to ignore errorprone checks "DoNotMock" and "DoNotMockAutoValue" because they fail with IndexOutOfBoundException trying to get the first argument of `Mockito.mock()`. :) --- src/main/java/org/mockito/Mockito.java | 17 +++++++++++++++++ src/test/java/org/mockito/MockitoTest.java | 8 ++++++++ .../org/mockito/kotlin/InlineClassTest.kt | 10 ++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index a221376287..169afad4d0 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1901,6 +1901,23 @@ public class Mockito extends ArgumentMatchers { */ public static final Answer RETURNS_SELF = Answers.RETURNS_SELF; + /** + * Creates mock object of requested class or interface. + *

+ * See examples in javadoc for {@link Mockito} class + * + * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. + * @return mock object + */ + public static T mock(T... reified) { + if (reified.length > 0) { + throw new IllegalArgumentException( + "Please don't pass any values here. Java will detect class automagically."); + } + Class classToMock = (Class) reified.getClass().getComponentType(); + return mock(classToMock, withSettings()); + } + /** * Creates mock object of given class or interface. *

diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index 8455011bb2..793e960476 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -133,4 +133,12 @@ public void shouldStartingMockSettingsContainDefaultBehavior() { // when / then assertThat(settings.getDefaultAnswer()).isEqualTo(Mockito.RETURNS_DEFAULTS); } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void automaticallyDetectsClassToMock() { + List mock = Mockito.mock(); + Mockito.when(mock.size()).thenReturn(42); + assertThat(mock.size()).isEqualTo(42); + } } diff --git a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt index a1a64ce0b0..daa60a9a9a 100644 --- a/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt +++ b/subprojects/kotlinTest/src/test/kotlin/org/mockito/kotlin/InlineClassTest.kt @@ -339,4 +339,14 @@ class InlineClassTest { verify(mock).returnsResult() } + + @Test + @SuppressWarnings("DoNotMock", "DoNotMockAutoValue") + fun automaticallyDetectsClassToMock() { + val mock: WithResult = mock() + + `when`(mock.returnsResult()).thenReturn(Result.success("OK")) + + assertEquals("OK", mock.returnsResult().getOrNull()) + } } From adf07fe5670aec2c2532a81075e2b4a144d2aeaa Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Sun, 13 Nov 2022 12:09:16 +0200 Subject: [PATCH 2/6] add javadoc Section 5.4 describing new method `mock()`. --- src/main/java/org/mockito/Mockito.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 169afad4d0..18118c6caa 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -107,6 +107,7 @@ * 51. New API for marking classes as unmockable (Since 4.1.0)
* 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
* 53. Specifying mock maker for individual mocks (Since 4.8.0)
+ * 54. Mocking without specifying class (Since 4.9.0)
* * *

0. Migrating to Mockito 2

@@ -1639,6 +1640,25 @@ * Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS)); * * + *

54. + * Mocking without specifying class (Since 4.9.0)

+ * + *

+ * Starting from 4.9.0, there is a shorter form of creating a mock. + *

+ * + * Instead of calling method {@link Mockito#mock(Class)} with Class parameter, you can now + * now call method {@code mock()} without parameters: + * + *

+ *   Foo foo = Mockito.mock();
+ *   Bar bar = Mockito.mock();
+ * 
+ * + * Mockito will automatically detect the needed class. + *

+ * P.S. Works only if you assign result of {@code mock()} to a variable or field. Only then Java compiler can know the type of that variable. + *

*/ @CheckReturnValue @SuppressWarnings("unchecked") @@ -1908,6 +1928,7 @@ public class Mockito extends ArgumentMatchers { * * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. * @return mock object + * @since 4.9.0 */ public static T mock(T... reified) { if (reified.length > 0) { From 2fa44f7e941f049a8be20f91531de3530aa6b0c8 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Sun, 13 Nov 2022 12:10:30 +0200 Subject: [PATCH 3/6] add negative test for passing arguments to new mock() method --- src/test/java/org/mockito/MockitoTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index 793e960476..de0fd93b4a 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -4,6 +4,7 @@ */ package org.mockito; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.times; @@ -141,4 +142,15 @@ public void automaticallyDetectsClassToMock() { Mockito.when(mock.size()).thenReturn(42); assertThat(mock.size()).isEqualTo(42); } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void newMockMethod_shouldNotBeCalledWithParameters() { + assertThatThrownBy( + () -> { + Mockito.mock(asList("1", "2"), asList("3", "4")); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); + } } From 27807bc69b3dce86103781e0dfed0c1bb8b3bde7 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Sun, 13 Nov 2022 12:24:49 +0200 Subject: [PATCH 4/6] add parameterless method `Mockito.spy()` ... similarly to `Mockito.mock()` --- src/main/java/org/mockito/Mockito.java | 43 +++++++++++++++++----- src/test/java/org/mockito/MockitoTest.java | 30 ++++++++++++--- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 18118c6caa..22fe32284e 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -33,7 +33,11 @@ import org.mockito.stubbing.OngoingStubbing; import org.mockito.stubbing.Stubber; import org.mockito.stubbing.VoidAnswer1; -import org.mockito.verification.*; +import org.mockito.verification.After; +import org.mockito.verification.Timeout; +import org.mockito.verification.VerificationAfterDelay; +import org.mockito.verification.VerificationMode; +import org.mockito.verification.VerificationWithTimeout; import java.util.function.Function; @@ -107,7 +111,7 @@ * 51. New API for marking classes as unmockable (Since 4.1.0)
* 52. New strictness attribute for @Mock annotation and MockSettings.strictness() methods (Since 4.6.0)
* 53. Specifying mock maker for individual mocks (Since 4.8.0)
- * 54. Mocking without specifying class (Since 4.9.0)
+ * 54. Mocking/spying without specifying class (Since 4.9.0)
* * *

0. Migrating to Mockito 2

@@ -1641,23 +1645,24 @@ * * *

54. - * Mocking without specifying class (Since 4.9.0)

+ * Mocking/spying without specifying class (Since 4.9.0) * *

- * Starting from 4.9.0, there is a shorter form of creating a mock. + * Starting from 4.9.0, there is a shorter form of creating a mock or spy. *

* - * Instead of calling method {@link Mockito#mock(Class)} with Class parameter, you can now - * now call method {@code mock()} without parameters: + * Instead of calling method {@link Mockito#mock(Class)} or {@link Mockito#spy(Class)} with Class parameter, you can now + * now call method {@code mock()} or {@code spy()} without parameters: * *

  *   Foo foo = Mockito.mock();
- *   Bar bar = Mockito.mock();
+ *   Bar bar = Mockito.spy();
  * 
* * Mockito will automatically detect the needed class. *

- * P.S. Works only if you assign result of {@code mock()} to a variable or field. Only then Java compiler can know the type of that variable. + * P.S. Works only if you assign result of {@code mock()} or {@code spy()} to a variable or field. + * Only then Java compiler can know the type of that variable. *

*/ @CheckReturnValue @@ -1935,8 +1940,7 @@ public static T mock(T... reified) { throw new IllegalArgumentException( "Please don't pass any values here. Java will detect class automagically."); } - Class classToMock = (Class) reified.getClass().getComponentType(); - return mock(classToMock, withSettings()); + return mock(getClassOf(reified), withSettings()); } /** @@ -2153,6 +2157,25 @@ public static T spy(Class classToSpy) { classToSpy, withSettings().useConstructor().defaultAnswer(CALLS_REAL_METHODS)); } + /** + * Please refer to the documentation of {@link #spy(Class)}. + * + * @param reified don't pass any values to it. It's a trick to detect the class/interface you want to mock. + * @return spy object + * @since 4.9.0 + */ + public static T spy(T... reified) { + if (reified.length > 0) { + throw new IllegalArgumentException( + "Please don't pass any values here. Java will detect class automagically."); + } + return spy(getClassOf(reified)); + } + + private static Class getClassOf(T[] array) { + return (Class) array.getClass().getComponentType(); + } + /** * Creates a thread-local mock controller for all static methods of the given class or interface. * The returned object's {@link MockedStatic#close()} method must be called upon completing the diff --git a/src/test/java/org/mockito/MockitoTest.java b/src/test/java/org/mockito/MockitoTest.java index de0fd93b4a..168acb8f40 100644 --- a/src/test/java/org/mockito/MockitoTest.java +++ b/src/test/java/org/mockito/MockitoTest.java @@ -147,10 +147,30 @@ public void automaticallyDetectsClassToMock() { @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) public void newMockMethod_shouldNotBeCalledWithParameters() { assertThatThrownBy( - () -> { - Mockito.mock(asList("1", "2"), asList("3", "4")); - }) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("Please don't pass any values here"); + () -> { + Mockito.mock(asList("1", "2"), asList("3", "4")); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); + } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void automaticallyDetectsClassToSpy() { + List mock = Mockito.spy(); + Mockito.when(mock.size()).thenReturn(42); + assertThat(mock.size()).isEqualTo(42); + assertThat(mock.get(0)).isNull(); + } + + @Test + @SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"}) + public void newSpyMethod_shouldNotBeCalledWithParameters() { + assertThatThrownBy( + () -> { + Mockito.spy(asList("1", "2"), asList("3", "4")); + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Please don't pass any values here"); } } From d903bd62257e6f0432cf6d61f3c4bad0e1dbcb4b Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Mon, 14 Nov 2022 22:08:43 +0200 Subject: [PATCH 5/6] Update src/main/java/org/mockito/Mockito.java Co-authored-by: Tim van der Lippe --- src/main/java/org/mockito/Mockito.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 22fe32284e..b756d8677e 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1647,10 +1647,6 @@ *

54. * Mocking/spying without specifying class (Since 4.9.0)

* - *

- * Starting from 4.9.0, there is a shorter form of creating a mock or spy. - *

- * * Instead of calling method {@link Mockito#mock(Class)} or {@link Mockito#spy(Class)} with Class parameter, you can now * now call method {@code mock()} or {@code spy()} without parameters: * From 7232399e113f1286d3b630f6754e4ecc0fff62e6 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Mon, 14 Nov 2022 22:08:58 +0200 Subject: [PATCH 6/6] Update src/main/java/org/mockito/Mockito.java Co-authored-by: Tim van der Lippe --- src/main/java/org/mockito/Mockito.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index b756d8677e..31eed046cf 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1657,8 +1657,9 @@ * * Mockito will automatically detect the needed class. *

- * P.S. Works only if you assign result of {@code mock()} or {@code spy()} to a variable or field. - * Only then Java compiler can know the type of that variable. + * It works only if you assign result of {@code mock()} or {@code spy()} to a variable or field with an explicit type. + * 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. *

*/ @CheckReturnValue