From fc3f0b1fba48a487fe2593803893f36969ab0f83 Mon Sep 17 00:00:00 2001 From: Johannes Spangenberg Date: Sat, 16 Jul 2022 15:28:20 +0200 Subject: [PATCH] fixup! Fixes #2626 : Introduce MockSettings.mockMaker --- src/main/java/org/mockito/Mock.java | 3 + .../org/mockito/internal/MockitoCore.java | 12 +- .../MockAnnotationProcessor.java | 5 + .../configuration/SpyAnnotationEngine.java | 2 + .../injection/SpyOnInjectedFieldsHandler.java | 1 + .../RetrieveGenericsForDefaultAnswers.java | 8 +- .../defaultanswers/ReturnsDeepStubs.java | 10 +- .../stubbing/defaultanswers/ReturnsMocks.java | 9 +- .../defaultanswers/ReturnsSmartNulls.java | 13 +- .../org/mockito/internal/util/MockUtil.java | 43 ++-- .../util/reflection/FieldInitializer.java | 7 +- .../java/org/mockito/CustomMockMakerTest.java | 199 +++++++++++++++--- .../mockito/internal/util/MockUtilTest.java | 8 +- .../CustomMockMakerAnnotationTest.java | 43 ++++ .../mockitoinline/ConstructionMockTest.java | 17 ++ 15 files changed, 298 insertions(+), 82 deletions(-) create mode 100644 src/test/java/org/mockitousage/annotation/CustomMockMakerAnnotationTest.java diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index e8469b830d..0331ae4b23 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -13,6 +13,7 @@ import java.lang.annotation.Target; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.plugins.MockMaker; import org.mockito.stubbing.Answer; /** @@ -124,6 +125,8 @@ */ Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT; + Class[] mockMaker() default {}; + enum Strictness { /** diff --git a/src/main/java/org/mockito/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java index 026803ad08..d3f23bf808 100644 --- a/src/main/java/org/mockito/internal/MockitoCore.java +++ b/src/main/java/org/mockito/internal/MockitoCore.java @@ -22,7 +22,6 @@ import static org.mockito.internal.util.MockUtil.getMockHandler; import static org.mockito.internal.util.MockUtil.isMock; import static org.mockito.internal.util.MockUtil.resetMock; -import static org.mockito.internal.util.MockUtil.typeMockabilityOf; import static org.mockito.internal.verification.VerificationModeFactory.noInteractions; import static org.mockito.internal.verification.VerificationModeFactory.noMoreInteractions; @@ -31,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Function; import org.mockito.InOrder; import org.mockito.MockSettings; @@ -67,10 +67,6 @@ import org.mockito.stubbing.Stubber; import org.mockito.verification.VerificationMode; -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - @SuppressWarnings("unchecked") public class MockitoCore { @@ -78,10 +74,6 @@ public class MockitoCore { private static final Set> MOCKABLE_CLASSES = Collections.synchronizedSet(new HashSet<>()); - public boolean isTypeMockable(Class typeToMock) { - return typeMockabilityOf(typeToMock).mockable(); - } - public T mock(Class typeToMock, MockSettings settings) { if (!(settings instanceof MockSettingsImpl)) { throw new IllegalArgumentException( @@ -166,7 +158,7 @@ public MockedConstruction mockConstruction( "Unexpected MockMaker '" + mockMaker.getCanonicalName() + "'\n" - + "You cannot override the MockMaker for construction mocks."); + + "At the moment, you cannot override the MockMaker for construction mocks."); } return impl.build(typeToMock); }; diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index 66c3f31d12..afa8aa0d56 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -53,6 +53,11 @@ 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]); + } // see @Mock answer default value mockSettings.defaultAnswer(annotation.answer()); diff --git a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java index a88ad63b84..13dfe356e4 100644 --- a/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java +++ b/src/main/java/org/mockito/internal/configuration/SpyAnnotationEngine.java @@ -83,6 +83,7 @@ public AutoCloseable process(Class context, Object testInstance) { } private static Object spyInstance(Field field, Object instance) { + // TODO: Should we also add an option to @Spy? return Mockito.mock( instance.getClass(), withSettings() @@ -93,6 +94,7 @@ private static Object spyInstance(Field field, Object instance) { private static Object spyNewInstance(Object testInstance, Field field) throws InstantiationException, IllegalAccessException, InvocationTargetException { + // TODO: Should we also add an option to @Spy? MockSettings settings = withSettings().defaultAnswer(CALLS_REAL_METHODS).name(field.getName()); Class type = field.getType(); diff --git a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java index 73f3004c19..1e5776c766 100644 --- a/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java +++ b/src/main/java/org/mockito/internal/configuration/injection/SpyOnInjectedFieldsHandler.java @@ -42,6 +42,7 @@ protected boolean processInjection(Field field, Object fieldOwner, Set m // B. protect against multiple use of MockitoAnnotations.openMocks() Mockito.reset(instance); } else { + // TODO: Should we also add an option to @Spy? Object mock = Mockito.mock( instance.getClass(), diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java index af0617153b..8b64a16916 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/RetrieveGenericsForDefaultAnswers.java @@ -8,7 +8,6 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import org.mockito.internal.MockitoCore; import org.mockito.internal.util.MockUtil; import org.mockito.internal.util.reflection.GenericMetadataSupport; import org.mockito.invocation.InvocationOnMock; @@ -16,8 +15,6 @@ final class RetrieveGenericsForDefaultAnswers { - private static final MockitoCore MOCKITO_CORE = new MockitoCore(); - static Object returnTypeForMockWithCorrectGenerics( InvocationOnMock invocation, AnswerCallback answerCallback) { Class type = invocation.getMethod().getReturnType(); @@ -38,8 +35,9 @@ static Object returnTypeForMockWithCorrectGenerics( } if (type != null) { - // TODO: Should we use the mockMaker of the mock? - if (!MOCKITO_CORE.isTypeMockable(type)) { + final MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + if (!MockUtil.typeMockabilityOf(type, mockSettings.getMockMaker()).mockable()) { return null; } diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java index 63226bfdf3..8356c1b79b 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java @@ -5,6 +5,7 @@ package org.mockito.internal.stubbing.defaultanswers; import static org.mockito.Mockito.withSettings; +import static org.mockito.internal.util.MockUtil.typeMockabilityOf; import java.io.IOException; import java.io.Serializable; @@ -50,10 +51,10 @@ public Object answer(InvocationOnMock invocation) throws Throwable { GenericMetadataSupport returnTypeGenericMetadata = actualParameterizedType(invocation.getMock()) .resolveGenericReturnType(invocation.getMethod()); + MockCreationSettings mockSettings = MockUtil.getMockSettings(invocation.getMock()); Class rawType = returnTypeGenericMetadata.rawType(); - // TODO: Should we use the mockMaker from the mock? - if (!mockitoCore().isTypeMockable(rawType)) { + if (!typeMockabilityOf(rawType, mockSettings.getMockMaker()).mockable()) { if (invocation.getMethod().getReturnType().equals(rawType)) { return delegate().answer(invocation); } else { @@ -120,7 +121,7 @@ private Object newDeepStubMock( private MockSettings withSettingsUsing( GenericMetadataSupport returnTypeGenericMetadata, - MockCreationSettings parentMockSettings) { + MockCreationSettings parentMockSettings) { MockSettings mockSettings = returnTypeGenericMetadata.hasRawExtraInterfaces() ? withSettings() @@ -128,7 +129,8 @@ private MockSettings withSettingsUsing( : withSettings(); return propagateSerializationSettings(mockSettings, parentMockSettings) - .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)); + .defaultAnswer(returnsDeepStubsAnswerUsing(returnTypeGenericMetadata)) + .mockMaker(parentMockSettings.getMockMaker()); } private MockSettings propagateSerializationSettings( diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java index 0ef6f0d954..c155780916 100755 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsMocks.java @@ -8,7 +8,9 @@ import org.mockito.Mockito; import org.mockito.internal.creation.MockSettingsImpl; +import org.mockito.internal.util.MockUtil; import org.mockito.invocation.InvocationOnMock; +import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Answer; public class ReturnsMocks implements Answer, Serializable { @@ -33,9 +35,14 @@ public Object apply(Class type) { return null; } + MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + return Mockito.mock( type, - new MockSettingsImpl().defaultAnswer(ReturnsMocks.this)); + new MockSettingsImpl<>() + .defaultAnswer(ReturnsMocks.this) + .mockMaker(mockSettings.getMockMaker())); } }); } diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java index 2402f364a6..3c40a98f4a 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsSmartNulls.java @@ -10,9 +10,12 @@ import java.io.Serializable; import org.mockito.Mockito; +import org.mockito.internal.creation.MockSettingsImpl; import org.mockito.internal.debugging.LocationImpl; +import org.mockito.internal.util.MockUtil; import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.Location; +import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Answer; /** @@ -56,8 +59,16 @@ public Object apply(Class type) { return null; } + MockCreationSettings mockSettings = + MockUtil.getMockSettings(invocation.getMock()); + Answer defaultAnswer = + new ThrowsSmartNullPointer(invocation, new LocationImpl()); + return Mockito.mock( - type, new ThrowsSmartNullPointer(invocation, new LocationImpl())); + type, + new MockSettingsImpl<>() + .defaultAnswer(defaultAnswer) + .mockMaker(mockSettings.getMockMaker())); } }); } diff --git a/src/main/java/org/mockito/internal/util/MockUtil.java b/src/main/java/org/mockito/internal/util/MockUtil.java index 4153bdb638..b0083d4848 100644 --- a/src/main/java/org/mockito/internal/util/MockUtil.java +++ b/src/main/java/org/mockito/internal/util/MockUtil.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.Map; -import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import static org.mockito.internal.handler.MockHandlerFactory.createMockHandler; @@ -29,35 +29,26 @@ public class MockUtil { private static final MockMaker defaultMockMaker = Plugins.getMockMaker(); - private static final Map, MockMaker> mockMakers; - - static { - mockMakers = Collections.synchronizedMap(new WeakHashMap<>()); - mockMakers.put(defaultMockMaker.getClass(), defaultMockMaker); - } + private static final Map, MockMaker> mockMakers = + new ConcurrentHashMap<>( + Collections.singletonMap(defaultMockMaker.getClass(), defaultMockMaker)); private MockUtil() {} private static MockMaker getMockMaker(Class type) { if (type == null) { return defaultMockMaker; - } else { - return mockMakers.computeIfAbsent( - type, - t -> { - try { - return t.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - throw new IllegalStateException( - "Failed to construct MockMaker: " + t, e); - } - }); } - } - - public static TypeMockability typeMockabilityOf(Class type) { - // TODO: Maybe we should replace all usages of this method with the method below - return defaultMockMaker.isTypeMockable(type); + return mockMakers.computeIfAbsent( + type, + t -> { + try { + return t.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException( + "Failed to construct MockMaker: " + t.getName(), e); + } + }); } public static TypeMockability typeMockabilityOf( @@ -99,7 +90,7 @@ public static void resetMock(Object mock) { } public static MockHandler getMockHandler(Object mock) { - MockHandler handler = getMockHandler0(mock); + MockHandler handler = getMockHandlerOrNull(mock); if (handler != null) { return handler; } else { @@ -131,10 +122,10 @@ public static boolean isMock(Object mock) { if (mock == null) { return false; } - return getMockHandler0(mock) != null; + return getMockHandlerOrNull(mock) != null; } - private static MockHandler getMockHandler0(Object mock) { + private static MockHandler getMockHandlerOrNull(Object mock) { if (mock == null) { throw new NotAMockException("Argument should be a mock, but is null!"); } diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java index 95c3e91819..8db0f816c1 100644 --- a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java +++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java @@ -259,8 +259,11 @@ public int compare(Constructor constructorA, Constructor constructorB) { private int countMockableParams(Constructor constructor) { int constructorMockableParamsSize = 0; for (Class aClass : constructor.getParameterTypes()) { - // TODO: Should we somehow use the mockMaker from the context? - if (MockUtil.typeMockabilityOf(aClass).mockable()) { + // TODO: I don't really understand the reason for this check here. + // It looks like we are not actually creating any mocks for these + // parameters, but might only use mocks which are already created + // and stored in some fields. + if (MockUtil.typeMockabilityOf(aClass, null).mockable()) { constructorMockableParamsSize++; } } diff --git a/src/test/java/org/mockito/CustomMockMakerTest.java b/src/test/java/org/mockito/CustomMockMakerTest.java index 07aba857ac..2632dec63d 100644 --- a/src/test/java/org/mockito/CustomMockMakerTest.java +++ b/src/test/java/org/mockito/CustomMockMakerTest.java @@ -4,47 +4,188 @@ */ package org.mockito; -import org.junit.Assert; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.withSettings; + +import java.util.Arrays; + 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 java.util.Arrays; +import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker; public final class CustomMockMakerTest { @Test - public void test_custom_mock_maker() throws Exception { - class TestClass { - String noop() { - return "UNUSED"; - } + public void test_normal_mock_uses_given_mock_maker() { + ClassWithFinalMethod inlineMock = + Mockito.mock( + ClassWithFinalMethod.class, + withSettings().mockMaker(InlineByteBuddyMockMaker.class)); + ClassWithFinalMethod subclassMock = + Mockito.mock( + ClassWithFinalMethod.class, + withSettings().mockMaker(ByteBuddyMockMaker.class)); - final String finalMethod() { - noop(); - return "ORIGINAL"; - } - } + Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + + assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal()); + assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal()); + assertEquals("MOCKED", subclassMock.nonFinal()); + } - TestClass inlineMock = + @Test + public void test_mockability_check_uses_given_mock_maker() { + assertNotNull( Mockito.mock( - TestClass.class, - Mockito.withSettings() - .outerInstance(this) - .useConstructor() - .mockMaker(InlineByteBuddyMockMaker.class)); - TestClass subclassMock = + FinalClass.class, + withSettings().mockMaker(InlineByteBuddyMockMaker.class))); + assertThrows( + MockitoException.class, + () -> + Mockito.mock( + FinalClass.class, + withSettings().mockMaker(ByteBuddyMockMaker.class))); + } + + @Test + public void test_deep_stups_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(InlineByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + Container subclassMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(ByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_DEEP_STUBS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.subContainer().finalClass()); + assertNull(inlineMock.finalClass().someMethod()); + assertNull(inlineMock.subContainer().finalClass().someMethod()); + assertNull(inlineMock.classWithFinalMethod().finalMethod()); + assertNull(inlineMock.subContainer().classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNull(subclassMock.subContainer().finalClass()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod()); + } + + @Test + public void test_returnd_mocks_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(InlineByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_MOCKS)); + Container subclassMock = Mockito.mock( - TestClass.class, - Mockito.withSettings() - .outerInstance(this) - .useConstructor() - .mockMaker(ByteBuddyMockMaker.class)); + Container.class, + withSettings() + .mockMaker(ByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_MOCKS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.subContainer().finalClass()); + assertEquals("", inlineMock.finalClass().someMethod()); + assertEquals("", inlineMock.subContainer().finalClass().someMethod()); + assertEquals("", inlineMock.classWithFinalMethod().finalMethod()); + assertEquals("", inlineMock.subContainer().classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNull(subclassMock.subContainer().finalClass()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + assertEquals("ORIGINAL", subclassMock.subContainer().classWithFinalMethod().finalMethod()); + } - for (TestClass mock : Arrays.asList(inlineMock, subclassMock)) { - Mockito.when(mock.finalMethod()).thenReturn("MOCKED"); + @Test + public void test_smart_nulls_inherit_mock_maker() { + Container inlineMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(InlineByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_SMART_NULLS)); + Container subclassMock = + Mockito.mock( + Container.class, + withSettings() + .mockMaker(ByteBuddyMockMaker.class) + .defaultAnswer(Answers.RETURNS_SMART_NULLS)); + + assertNotNull(inlineMock.finalClass()); + assertNotNull(inlineMock.classWithFinalMethod()); + assertThrows(SmartNullPointerException.class, () -> inlineMock.finalClass().someMethod()); + assertThrows( + SmartNullPointerException.class, + () -> inlineMock.classWithFinalMethod().finalMethod()); + + assertNull(subclassMock.finalClass()); + assertNotNull(subclassMock.classWithFinalMethod()); + assertEquals("ORIGINAL", subclassMock.classWithFinalMethod().finalMethod()); + } + + @Test + public void test_exception_when_mock_maker_cannot_be_instantiated() { + class InvalidMockMaker extends SubclassByteBuddyMockMaker { + // Local classes have an implicit constructor parameter, + // which makes them an invalid mock maker. + } + assertThatThrownBy( + () -> { + Mockito.mock( + Container.class, + withSettings().mockMaker(InvalidMockMaker.class)); + }) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("Failed to construct MockMaker") + .hasMessageContaining(InvalidMockMaker.class.getName()); + } + + private static final class FinalClass { + String someMethod() { + return "ORIGINAL"; + } + } + + private static class ClassWithFinalMethod { + final String finalMethod() { + return "ORIGINAL"; + } + + final String finalMethodCallingNonFinal() { + nonFinal(); + return "ORIGINAL"; + } + + String nonFinal() { + return "ORIGINAL"; + } + } + + private static class Container { + FinalClass finalClass() { + return new FinalClass(); } - Assert.assertEquals("MOCKED", inlineMock.finalMethod()); - Assert.assertEquals("ORIGINAL", subclassMock.finalMethod()); + ClassWithFinalMethod classWithFinalMethod() { + return new ClassWithFinalMethod(); + } + + Container subContainer() { + return new Container(); + } } } diff --git a/src/test/java/org/mockito/internal/util/MockUtilTest.java b/src/test/java/org/mockito/internal/util/MockUtilTest.java index 50cadb4c0c..834178cdec 100644 --- a/src/test/java/org/mockito/internal/util/MockUtilTest.java +++ b/src/test/java/org/mockito/internal/util/MockUtilTest.java @@ -100,12 +100,12 @@ interface SomeInterface {} @Test public void should_know_if_type_is_mockable() throws Exception { - Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class).mockable()) + Assertions.assertThat(MockUtil.typeMockabilityOf(FinalClass.class, null).mockable()) .isEqualTo(Plugins.getMockMaker().isTypeMockable(FinalClass.class).mockable()); - assertFalse(MockUtil.typeMockabilityOf(int.class).mockable()); + assertFalse(MockUtil.typeMockabilityOf(int.class, null).mockable()); - assertTrue(MockUtil.typeMockabilityOf(SomeClass.class).mockable()); - assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class).mockable()); + assertTrue(MockUtil.typeMockabilityOf(SomeClass.class, null).mockable()); + assertTrue(MockUtil.typeMockabilityOf(SomeInterface.class, null).mockable()); } } diff --git a/src/test/java/org/mockitousage/annotation/CustomMockMakerAnnotationTest.java b/src/test/java/org/mockitousage/annotation/CustomMockMakerAnnotationTest.java new file mode 100644 index 0000000000..4223635442 --- /dev/null +++ b/src/test/java/org/mockitousage/annotation/CustomMockMakerAnnotationTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.annotation; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker; +import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; +import org.mockitoutil.TestBase; + +public class CustomMockMakerAnnotationTest extends TestBase { + @Mock(mockMaker = InlineByteBuddyMockMaker.class) + ClassWithFinalMethod inlineMock; + + @Mock(mockMaker = ByteBuddyMockMaker.class) + ClassWithFinalMethod subclassMock; + + @Test + public void test_mock_uses_given_mock_maker() { + Mockito.when(inlineMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + Mockito.when(subclassMock.finalMethodCallingNonFinal()).thenReturn("MOCKED"); + + assertEquals("MOCKED", inlineMock.finalMethodCallingNonFinal()); + assertEquals("ORIGINAL", subclassMock.finalMethodCallingNonFinal()); + assertEquals("MOCKED", subclassMock.nonFinal()); + } + + private static class ClassWithFinalMethod { + final String finalMethodCallingNonFinal() { + nonFinal(); + return "ORIGINAL"; + } + + String nonFinal() { + return "ORIGINAL"; + } + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java b/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java index 7eb87c5702..58cf24566b 100644 --- a/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java +++ b/subprojects/inline/src/test/java/org/mockitoinline/ConstructionMockTest.java @@ -11,6 +11,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; @@ -19,6 +20,7 @@ import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoException; +import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; public final class ConstructionMockTest { @@ -154,6 +156,21 @@ public void testConstructionMockMustNotTargetAbstractClass() { .hasMessageContaining("It is not possible to construct primitive types or abstract types"); } + @Test + public void testConstructionMocksMustNotUseCustomMockMaker() { + assertThatThrownBy( + () -> { + try (MockedConstruction ignored = Mockito.mockConstruction( + Dummy.class, + withSettings().mockMaker(InlineByteBuddyMockMaker.class)) + ) { + new Dummy(); + } + }) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("you cannot override the MockMaker for construction mocks"); + } + static class Dummy {