Skip to content

Commit

Permalink
fixup! Fixes mockito#2626 : Introduce MockSettings.mockMaker
Browse files Browse the repository at this point in the history
  • Loading branch information
JojOatXGME committed Aug 28, 2022
1 parent a472ed0 commit ecbdd54
Show file tree
Hide file tree
Showing 17 changed files with 113 additions and 54 deletions.
3 changes: 1 addition & 2 deletions src/main/java/org/mockito/Mock.java
Expand Up @@ -13,7 +13,6 @@
import java.lang.annotation.Target;

import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.plugins.MockMaker;
import org.mockito.stubbing.Answer;

/**
Expand Down Expand Up @@ -125,7 +124,7 @@
*/
Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT;

Class<? extends MockMaker>[] mockMaker() default {};
String mockMaker() default "";

enum Strictness {

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/mockito/MockMakers.java
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2022 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito;

public final class MockMakers {
public static final String INLINE = "mock-maker-inline";
public static final String PROXY = "mock-maker-proxy";
public static final String SUBCLASS = "mock-maker-subclass";

private MockMakers() {}
}
3 changes: 1 addition & 2 deletions src/main/java/org/mockito/MockSettings.java
Expand Up @@ -15,7 +15,6 @@
import org.mockito.listeners.VerificationStartedListener;
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.SerializableMode;
import org.mockito.plugins.MockMaker;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

Expand Down Expand Up @@ -383,5 +382,5 @@ public interface MockSettings extends Serializable {
*/
MockSettings strictness(Strictness strictness);

MockSettings mockMaker(Class<? extends MockMaker> mockMaker);
MockSettings mockMaker(String mockMaker);
}
4 changes: 2 additions & 2 deletions src/main/java/org/mockito/internal/MockitoCore.java
Expand Up @@ -152,11 +152,11 @@ public <T> MockedConstruction<T> mockConstruction(
+ "At the moment, you cannot provide your own implementations of that class.");
}
MockSettingsImpl impl = MockSettingsImpl.class.cast(value);
Class<? extends MockMaker> mockMaker = impl.getMockMaker();
String mockMaker = impl.getMockMaker();
if (mockMaker != null) {
throw new IllegalArgumentException(
"Unexpected MockMaker '"
+ mockMaker.getCanonicalName()
+ mockMaker
+ "'\n"
+ "At the moment, you cannot override the MockMaker for construction mocks.");
}
Expand Down
Expand Up @@ -53,10 +53,8 @@ public static Object processAnnotationForMock(
if (annotation.strictness() != Mock.Strictness.TEST_LEVEL_DEFAULT) {
mockSettings.strictness(Strictness.valueOf(annotation.strictness().toString()));
}
if (annotation.mockMaker().length > 0) {
// TODO: Use a special placeholder class for the default, instead of using an array?
assert annotation.mockMaker().length == 1 : "cannon use multiple mock makers";
mockSettings.mockMaker(annotation.mockMaker()[0]);
if (!annotation.mockMaker().isEmpty()) {
mockSettings.mockMaker(annotation.mockMaker());
}

// see @Mock answer default value
Expand Down
Expand Up @@ -6,6 +6,8 @@

import java.util.HashMap;
import java.util.Map;

import org.mockito.MockMakers;
import org.mockito.plugins.AnnotationEngine;
import org.mockito.plugins.DoNotMockEnforcer;
import org.mockito.plugins.InstantiatorProvider2;
Expand All @@ -19,8 +21,9 @@
class DefaultMockitoPlugins implements MockitoPlugins {

private static final Map<String, String> DEFAULT_PLUGINS = new HashMap<>();
static final String INLINE_ALIAS = "mock-maker-inline";
static final String PROXY_ALIAS = "mock-maker-proxy";
static final String INLINE_ALIAS = MockMakers.INLINE;
static final String PROXY_ALIAS = MockMakers.PROXY;
static final String SUBCLASS_ALIAS = MockMakers.SUBCLASS;
static final String MODULE_ALIAS = "member-accessor-module";

static {
Expand All @@ -41,6 +44,8 @@ class DefaultMockitoPlugins implements MockitoPlugins {
DEFAULT_PLUGINS.put(
INLINE_ALIAS, "org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker");
DEFAULT_PLUGINS.put(PROXY_ALIAS, "org.mockito.internal.creation.proxy.ProxyMockMaker");
DEFAULT_PLUGINS.put(
SUBCLASS_ALIAS, "org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker");
DEFAULT_PLUGINS.put(
MockitoLogger.class.getName(), "org.mockito.internal.util.ConsoleMockitoLogger");
DEFAULT_PLUGINS.put(
Expand Down
Expand Up @@ -24,7 +24,8 @@ class PluginRegistry {
new PluginLoader(
pluginSwitch,
DefaultMockitoPlugins.INLINE_ALIAS,
DefaultMockitoPlugins.PROXY_ALIAS)
DefaultMockitoPlugins.PROXY_ALIAS,
DefaultMockitoPlugins.SUBCLASS_ALIAS)
.loadPlugin(MockMaker.class);

private final MemberAccessor memberAccessor =
Expand Down
Expand Up @@ -34,7 +34,6 @@
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.MockName;
import org.mockito.mock.SerializableMode;
import org.mockito.plugins.MockMaker;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

Expand Down Expand Up @@ -256,7 +255,7 @@ public MockSettings strictness(Strictness strictness) {
}

@Override
public MockSettings mockMaker(Class<? extends MockMaker> mockMaker) {
public MockSettings mockMaker(String mockMaker) {
this.mockMaker = mockMaker;
return this;
}
Expand Down
Expand Up @@ -18,7 +18,6 @@
import org.mockito.mock.MockCreationSettings;
import org.mockito.mock.MockName;
import org.mockito.mock.SerializableMode;
import org.mockito.plugins.MockMaker;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

Expand Down Expand Up @@ -47,7 +46,7 @@ public class CreationSettings<T> implements MockCreationSettings<T>, Serializabl
private Object outerClassInstance;
private Object[] constructorArgs;
protected Strictness strictness = null;
protected Class<? extends MockMaker> mockMaker;
protected String mockMaker;

public CreationSettings() {}

Expand Down Expand Up @@ -183,7 +182,7 @@ public Strictness getStrictness() {
}

@Override
public Class<? extends MockMaker> getMockMaker() {
public String getMockMaker() {
return mockMaker;
}
}
Expand Up @@ -13,13 +13,12 @@
import java.util.Collection;

import org.mockito.mock.SerializableMode;
import org.mockito.plugins.MockMaker;
import org.mockito.plugins.MockMaker.TypeMockability;

@SuppressWarnings("unchecked")
public class MockCreationValidator {

public void validateType(Class<?> classToMock, Class<? extends MockMaker> mockMaker) {
public void validateType(Class<?> classToMock, String mockMaker) {
TypeMockability typeMockability = MockUtil.typeMockabilityOf(classToMock, mockMaker);
if (!typeMockability.mockable()) {
throw cannotMockClass(classToMock, typeMockability.nonMockableReason());
Expand Down
40 changes: 36 additions & 4 deletions src/main/java/org/mockito/internal/util/MockUtil.java
Expand Up @@ -4,10 +4,14 @@
*/
package org.mockito.internal.util;

import org.mockito.MockMakers;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import org.mockito.exceptions.misusing.NotAMockException;
import org.mockito.internal.configuration.plugins.Plugins;
import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker;
import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
import org.mockito.internal.creation.proxy.ProxyMockMaker;
import org.mockito.internal.creation.settings.CreationSettings;
import org.mockito.internal.stubbing.InvocationContainerImpl;
import org.mockito.internal.util.reflection.LenientCopyTool;
Expand Down Expand Up @@ -35,10 +39,39 @@ public class MockUtil {

private MockUtil() {}

private static MockMaker getMockMaker(Class<? extends MockMaker> type) {
if (type == null) {
private static MockMaker getMockMaker(String mockMaker) {
if (mockMaker == null) {
return defaultMockMaker;
}

// TODO: Share logic with PluginInitializer.loadImpl
Class<? extends MockMaker> type;
switch (mockMaker) {
case MockMakers.INLINE:
type = InlineByteBuddyMockMaker.class;
break;
case MockMakers.PROXY:
type = ProxyMockMaker.class;
break;
case MockMakers.SUBCLASS:
type = ByteBuddyMockMaker.class;
break;
default:
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = ClassLoader.getSystemClassLoader();
}
try {
type = loader.loadClass(mockMaker).asSubclass(MockMaker.class);
} catch (Exception e) {
throw new IllegalStateException(
"Failed to load MockMaker: " + mockMaker, e);
}
break;
}
}

return mockMakers.computeIfAbsent(
type,
t -> {
Expand All @@ -51,8 +84,7 @@ private static MockMaker getMockMaker(Class<? extends MockMaker> type) {
});
}

public static TypeMockability typeMockabilityOf(
Class<?> type, Class<? extends MockMaker> mockMaker) {
public static TypeMockability typeMockabilityOf(Class<?> type, String mockMaker) {
return getMockMaker(mockMaker).isTypeMockable(type);
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/mockito/mock/MockCreationSettings.java
Expand Up @@ -141,5 +141,5 @@ public interface MockCreationSettings<T> {
* The {@link MockMaker} which shall by used instead of the default. When
* the return value is {@code null}, the default shall be used.
*/
Class<? extends MockMaker> getMockMaker();
String getMockMaker();
}
2 changes: 1 addition & 1 deletion src/main/java/org/mockito/plugins/MockMaker.java
Expand Up @@ -49,7 +49,7 @@
* <h3>Using the MockSettings of individual mocks</h3>
*
* <p>If you want to use your {@code AwesomeMockMaker} only for a specific mock,
* you can specify it using {@link MockSettings#mockMaker(Class)}.</p>
* you can specify it using {@link MockSettings#mockMaker(String)}.</p>
* <pre>
* Object mock = Mockito.mock(Object.class, Mockito.withSettings()
* .mockMaker(AwesomeMockMaker.class));
Expand Down
Expand Up @@ -14,21 +14,19 @@
import org.junit.Test;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.verification.SmartNullPointerException;
import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker;
import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker;
import org.mockito.invocation.MockHandler;
import org.mockito.mock.MockCreationSettings;

public final class CustomMockMakerTest {
public final class ProgrammaticMockMakerTest {
@Test
public void test_normal_mock_uses_given_mock_maker() {
ClassWithFinalMethod inlineMock =
Mockito.mock(
ClassWithFinalMethod.class,
withSettings().mockMaker(InlineByteBuddyMockMaker.class));
ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.INLINE));
ClassWithFinalMethod subclassMock =
Mockito.mock(
ClassWithFinalMethod.class,
withSettings().mockMaker(ByteBuddyMockMaker.class));
ClassWithFinalMethod.class, withSettings().mockMaker(MockMakers.SUBCLASS));

Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED");
Expand All @@ -40,16 +38,12 @@ public void test_normal_mock_uses_given_mock_maker() {

@Test
public void test_mockability_check_uses_given_mock_maker() {
assertNotNull(
Mockito.mock(
FinalClass.class,
withSettings().mockMaker(InlineByteBuddyMockMaker.class)));
assertNotNull(Mockito.mock(FinalClass.class, withSettings().mockMaker(MockMakers.INLINE)));
assertThrows(
MockitoException.class,
() ->
Mockito.mock(
FinalClass.class,
withSettings().mockMaker(ByteBuddyMockMaker.class)));
FinalClass.class, withSettings().mockMaker(MockMakers.SUBCLASS)));
}

@Test
Expand All @@ -58,13 +52,13 @@ public void test_deep_stups_inherit_mock_maker() {
Mockito.mock(
Container.class,
withSettings()
.mockMaker(InlineByteBuddyMockMaker.class)
.mockMaker(MockMakers.INLINE)
.defaultAnswer(Answers.RETURNS_DEEP_STUBS));
Container subclassMock =
Mockito.mock(
Container.class,
withSettings()
.mockMaker(ByteBuddyMockMaker.class)
.mockMaker(MockMakers.SUBCLASS)
.defaultAnswer(Answers.RETURNS_DEEP_STUBS));

assertNotNull(inlineMock.finalClass());
Expand All @@ -81,18 +75,18 @@ public void test_deep_stups_inherit_mock_maker() {
}

@Test
public void test_returnd_mocks_inherit_mock_maker() {
public void test_returned_mocks_inherit_mock_maker() {
Container inlineMock =
Mockito.mock(
Container.class,
withSettings()
.mockMaker(InlineByteBuddyMockMaker.class)
.mockMaker(MockMakers.INLINE)
.defaultAnswer(Answers.RETURNS_MOCKS));
Container subclassMock =
Mockito.mock(
Container.class,
withSettings()
.mockMaker(ByteBuddyMockMaker.class)
.mockMaker(MockMakers.SUBCLASS)
.defaultAnswer(Answers.RETURNS_MOCKS));

assertNotNull(inlineMock.finalClass());
Expand All @@ -114,13 +108,13 @@ public void test_smart_nulls_inherit_mock_maker() {
Mockito.mock(
Container.class,
withSettings()
.mockMaker(InlineByteBuddyMockMaker.class)
.mockMaker(MockMakers.INLINE)
.defaultAnswer(Answers.RETURNS_SMART_NULLS));
Container subclassMock =
Mockito.mock(
Container.class,
withSettings()
.mockMaker(ByteBuddyMockMaker.class)
.mockMaker(MockMakers.SUBCLASS)
.defaultAnswer(Answers.RETURNS_SMART_NULLS));

assertNotNull(inlineMock.finalClass());
Expand All @@ -135,6 +129,17 @@ public void test_smart_nulls_inherit_mock_maker() {
assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod());
}

@Test
public void test_custom_mock_maker() {
assertThatThrownBy(
() -> {
Mockito.mock(
Container.class,
withSettings().mockMaker(CustomMockMaker.class.getName()));
})
.hasMessage("CUSTOM MOCK MAKER");
}

@Test
public void test_exception_when_mock_maker_cannot_be_instantiated() {
class InvalidMockMaker extends SubclassByteBuddyMockMaker {
Expand All @@ -145,7 +150,7 @@ class InvalidMockMaker extends SubclassByteBuddyMockMaker {
() -> {
Mockito.mock(
Container.class,
withSettings().mockMaker(InvalidMockMaker.class));
withSettings().mockMaker(InvalidMockMaker.class.getName()));
})
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Failed to construct MockMaker")
Expand Down Expand Up @@ -186,4 +191,11 @@ Container subContainer() {
return new Container();
}
}

public static class CustomMockMaker extends SubclassByteBuddyMockMaker {
@Override
public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
throw new RuntimeException("CUSTOM MOCK MAKER");
}
}
}

0 comments on commit ecbdd54

Please sign in to comment.