Skip to content

Commit

Permalink
Feat: reified mock overloads (#2882)
Browse files Browse the repository at this point in the history
Added reified mock overload methods to match the methods
that consume an explicit Class<T> object.

The following methods have been added:

- Mockito.<T>mock(String, T...) - Sets the mock name.
- Mockito.<T>mock(Answer, T...) - Sets the default answer.
- Mockito.<T>mock(WithSettings, T...) - Provides custom settings.

I have also added a case where passing null varargs in would
result in a NullPointerException previously. Now, it will tell
the user that they should not be providing these varargs at all.

These overloads should not conflict with any existing usages of
this API in the intended way, as the assumption is that a user
probably isn't going to want to be mocking Mockito internals,
Strings, or WithSettings objects anyway. If they do need to
achieve this, then this is still accessible via the existing
Mockito.<T>mock(Class<T>) methods anyway.
  • Loading branch information
ascopes committed Jan 21, 2023
1 parent 96452fa commit 91223f8
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 6 deletions.
59 changes: 53 additions & 6 deletions src/main/java/org/mockito/Mockito.java
Expand Up @@ -1924,21 +1924,68 @@ public class Mockito extends ArgumentMatchers {
public static final Answer<Object> RETURNS_SELF = Answers.RETURNS_SELF;

/**
* Creates mock object of requested class or interface.
* Creates a mock object of the requested class or interface.
* <p>
* See examples in javadoc for {@link Mockito} class
* See examples in javadoc for the {@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
* @param reified don't pass any values to it. It's a trick to detect the class/interface you
* want to mock.
* @return the mock object.
* @since 4.9.0
*/
@SafeVarargs
public static <T> T mock(T... reified) {
if (reified.length > 0) {
return mock(withSettings(), reified);
}

/**
* Creates a mock object of the requested class or interface with the given default answer.
* <p>
* See examples in javadoc for the {@link Mockito} class.
*
* @param defaultAnswer the default answer to use.
* @param reified don't pass any values to it. It's a trick to detect the class/interface you
* want to mock.
* @return the mock object.
*/
@SafeVarargs
public static <T> T mock(@SuppressWarnings("rawtypes") Answer defaultAnswer, T... reified) {
return mock(withSettings().defaultAnswer(defaultAnswer), reified);
}

/**
* Creates a mock object of the requested class or interface with the given name.
* <p>
* See examples in javadoc for the {@link Mockito} class.
*
* @param name the mock name to use.
* @param reified don't pass any values to it. It's a trick to detect the class/interface you
* want to mock.
* @return the mock object.
*/
@SafeVarargs
public static <T> T mock(String name, T... reified) {
return mock(withSettings().name(name).defaultAnswer(RETURNS_DEFAULTS), reified);
}

/**
* Creates a mock object of the requested class or interface with the given settings.
* <p>
* See examples in javadoc for the {@link Mockito} class.
*
* @param settings the mock settings to use.
* @param reified don't pass any values to it. It's a trick to detect the class/interface you
* want to mock.
* @return the mock object.
*/
@SafeVarargs
public static <T> T mock(MockSettings settings, T... reified) {
if (reified == null || reified.length > 0) {
throw new IllegalArgumentException(
"Please don't pass any values here. Java will detect class automagically.");
}
return mock(getClassOf(reified), withSettings());

return mock(getClassOf(reified), settings);
}

/**
Expand Down
44 changes: 44 additions & 0 deletions src/test/java/org/mockito/MockitoTest.java
Expand Up @@ -21,6 +21,7 @@
import org.mockito.exceptions.misusing.NullInsteadOfMockException;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.creation.MockSettingsImpl;
import org.mockito.listeners.InvocationListener;
import org.mockito.plugins.InlineMockMaker;

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -166,6 +167,49 @@ public void automaticallyDetectsClassToMock() {
assertThat(mock.size()).isEqualTo(42);
}

@Test
@SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
public void newMockMethod_shouldNotBeCalledWithNullParameters() {
assertThatThrownBy(
() -> {
Mockito.mock((Object[]) null);
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessageStartingWith("Please don't pass any values here");
}

@Test
public void reifiedMockMethodWithNameSetsTheExpectedName() {
List<String> mock = Mockito.mock("My super cool new mock");
assertThat(mock).hasToString("My super cool new mock");
}

@Test
public void reifiedMockMethodWithDefaultAnswerSetsTheDefaultAnswer() {
abstract class Something {
abstract Something somethingElse();
}

Something something = Mockito.mock(Answers.RETURNS_SELF);

assertThat(something.somethingElse()).isSameAs(something);
}

@Test
public void reifiedMockMethodWithSettingsAppliesTheSettings() {

InvocationListener invocationListener = Mockito.mock(InvocationListener.class);

List<Object> mock =
Mockito.mock(
Mockito.withSettings()
.name("my name here")
.invocationListeners(invocationListener));

assertThat(mock).hasToString("my name here");
Mockito.verify(invocationListener).reportInvocation(ArgumentMatchers.any());
}

@Test
@SuppressWarnings({"DoNotMock", "DoNotMockAutoValue"})
public void newMockMethod_shouldNotBeCalledWithParameters() {
Expand Down

0 comments on commit 91223f8

Please sign in to comment.