diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java
index a221376287..31eed046cf 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,6 +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/spying without specifying class (Since 4.9.0)
*
*
*
@@ -1639,6 +1644,23 @@
* Foo mock = Mockito.mock(Foo.class, withSettings().mockMaker(MockMakers.SUBCLASS));
*
*
+ *
+ *
+ * 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.spy();
+ *
+ *
+ * Mockito will automatically detect the needed class.
+ *
+ * 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
@SuppressWarnings("unchecked")
@@ -1901,6 +1923,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
+ * @since 4.9.0
+ */
+ 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.");
+ }
+ return mock(getClassOf(reified), withSettings());
+ }
+
/**
* Creates mock object of given class or interface.
*
@@ -2115,6 +2154,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 8455011bb2..168acb8f40 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;
@@ -133,4 +134,43 @@ 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);
+ }
+
+ @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");
+ }
+
+ @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");
+ }
}
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())
+ }
}