From c2ce6a666996013720fc71ac1e709db5a8a78822 Mon Sep 17 00:00:00 2001 From: saurabh7248 Date: Fri, 30 Jul 2021 10:38:03 +0530 Subject: [PATCH 1/2] Fixes #2331 Has conditionally on basis of whether default answer is pass to actual methods made hash code and equals pass to actual methods. --- .../InlineDelegateByteBuddyMockMaker.java | 3 ++- .../creation/bytebuddy/MockFeatures.java | 12 +++++++++--- .../bytebuddy/SubclassByteBuddyMockMaker.java | 3 ++- .../bytebuddy/SubclassBytecodeGenerator.java | 19 +++++++++++++++---- .../invocation/InvocationMatcher.java | 2 +- .../TypeCachingMockBytecodeGeneratorTest.java | 16 +++++++++++----- .../spies/SpyingOnRealObjectsTest.java | 1 + 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java index 553a02e248..7178927cd9 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineDelegateByteBuddyMockMaker.java @@ -393,7 +393,8 @@ public Class createMockType(MockCreationSettings settings) { settings.getTypeToMock(), settings.getExtraInterfaces(), settings.getSerializableMode(), - settings.isStripAnnotations())); + settings.isStripAnnotations(), + settings.getDefaultAnswer())); } catch (Exception bytecodeGenerationFailed) { throw prettifyFailure(settings, bytecodeGenerationFailed); } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/MockFeatures.java b/src/main/java/org/mockito/internal/creation/bytebuddy/MockFeatures.java index 1dbfba1334..ca27be01e1 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/MockFeatures.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/MockFeatures.java @@ -8,6 +8,7 @@ import java.util.Set; import org.mockito.mock.SerializableMode; +import org.mockito.stubbing.Answer; class MockFeatures { @@ -15,23 +16,28 @@ class MockFeatures { final Set> interfaces; final SerializableMode serializableMode; final boolean stripAnnotations; + final Answer defaultAnswer; private MockFeatures( Class mockedType, Set> interfaces, SerializableMode serializableMode, - boolean stripAnnotations) { + boolean stripAnnotations, + Answer defaultAnswer) { this.mockedType = mockedType; this.interfaces = Collections.unmodifiableSet(interfaces); this.serializableMode = serializableMode; this.stripAnnotations = stripAnnotations; + this.defaultAnswer = defaultAnswer; } public static MockFeatures withMockFeatures( Class mockedType, Set> interfaces, SerializableMode serializableMode, - boolean stripAnnotations) { - return new MockFeatures(mockedType, interfaces, serializableMode, stripAnnotations); + boolean stripAnnotations, + Answer defaultAnswer) { + return new MockFeatures( + mockedType, interfaces, serializableMode, stripAnnotations, defaultAnswer); } } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java index 8bf72feb89..dc1e81752c 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassByteBuddyMockMaker.java @@ -79,7 +79,8 @@ public Class createMockType(MockCreationSettings settings) { settings.getTypeToMock(), settings.getExtraInterfaces(), settings.getSerializableMode(), - settings.isStripAnnotations())); + settings.isStripAnnotations(), + settings.getDefaultAnswer())); } catch (Exception bytecodeGenerationFailed) { throw prettifyFailure(settings, bytecodeGenerationFailed); } diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java index 3dadd3fa46..46ecb7868a 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/SubclassBytecodeGenerator.java @@ -44,6 +44,7 @@ import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.attribute.MethodAttributeAppender; import net.bytebuddy.matcher.ElementMatcher; +import org.mockito.Answers; import org.mockito.codegen.InjectionBase; import org.mockito.exceptions.base.MockitoException; import org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport.CrossClassLoaderSerializableMock; @@ -237,14 +238,24 @@ public Class mockClass(MockFeatures features) { features.stripAnnotations ? MethodAttributeAppender.NoOp.INSTANCE : INCLUDING_RECEIVER) - .method(isHashCode()) - .intercept(hashCode) - .method(isEquals()) - .intercept(equals) .serialVersionUid(42L) .defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE) .implement(MockAccess.class) .intercept(FieldAccessor.ofBeanProperty()); + + if (features.defaultAnswer != Answers.CALLS_REAL_METHODS) { + builder = + builder.method(isHashCode()) + .intercept(hashCode) + .method(isEquals()) + .intercept(equals); + } else { + builder = + builder.method(isHashCode()) + .intercept(dispatcher) + .method(isEquals()) + .intercept(dispatcher); + } if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) { builder = builder.implement(CrossClassLoaderSerializableMock.class) diff --git a/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java b/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java index ef475e60c6..6cc7a8c49f 100644 --- a/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java +++ b/src/main/java/org/mockito/internal/invocation/InvocationMatcher.java @@ -77,7 +77,7 @@ public String toString() { @Override public boolean matches(Invocation candidate) { - return invocation.getMock().equals(candidate.getMock()) + return invocation.getMock() == candidate.getMock() && hasSameMethod(candidate) && argumentsMatch(candidate); } diff --git a/src/test/java/org/mockito/internal/creation/bytebuddy/TypeCachingMockBytecodeGeneratorTest.java b/src/test/java/org/mockito/internal/creation/bytebuddy/TypeCachingMockBytecodeGeneratorTest.java index d0ed772a68..6ffe72950f 100644 --- a/src/test/java/org/mockito/internal/creation/bytebuddy/TypeCachingMockBytecodeGeneratorTest.java +++ b/src/test/java/org/mockito/internal/creation/bytebuddy/TypeCachingMockBytecodeGeneratorTest.java @@ -18,6 +18,7 @@ import org.junit.Before; import org.junit.Test; +import org.mockito.Answers; import org.mockito.mock.SerializableMode; import org.mockitoutil.VmArgAssumptions; @@ -46,7 +47,8 @@ public void ensure_cache_is_cleared_if_no_reference_to_classloader_and_classes() classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), Collections.>emptySet(), SerializableMode.NONE, - false)); + false, + Answers.RETURNS_DEFAULTS)); ReferenceQueue referenceQueue = new ReferenceQueue(); Reference typeReference = @@ -79,7 +81,8 @@ public void ensure_cache_returns_same_instance() throws Exception { classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), Collections.>emptySet(), SerializableMode.NONE, - false)); + false, + Answers.RETURNS_DEFAULTS)); Class other_mock_type = cachingMockBytecodeGenerator.mockClass( @@ -87,7 +90,8 @@ public void ensure_cache_returns_same_instance() throws Exception { classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), Collections.>emptySet(), SerializableMode.NONE, - false)); + false, + Answers.RETURNS_DEFAULTS)); assertThat(other_mock_type).isSameAs(the_mock_type); @@ -123,7 +127,8 @@ public void ensure_cache_returns_different_instance_serializableMode() throws Ex classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), Collections.>emptySet(), SerializableMode.NONE, - false)); + false, + Answers.RETURNS_DEFAULTS)); Class other_mock_type = cachingMockBytecodeGenerator.mockClass( @@ -131,7 +136,8 @@ public void ensure_cache_returns_different_instance_serializableMode() throws Ex classloader_with_life_shorter_than_cache.loadClass("foo.Bar"), Collections.>emptySet(), SerializableMode.BASIC, - false)); + false, + Answers.RETURNS_DEFAULTS)); assertThat(other_mock_type).isNotSameAs(the_mock_type); } diff --git a/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java b/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java index a3ed58cfd4..0511965720 100644 --- a/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java +++ b/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java @@ -9,6 +9,7 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; From 6886b0f6a286622620ba3c6f875c215d25a79d4f Mon Sep 17 00:00:00 2001 From: saurabh7248 Date: Fri, 30 Jul 2021 10:55:31 +0530 Subject: [PATCH 2/2] Fixes #2331 Has conditionally on basis of whether default answer is pass to actual methods made hash code and equals pass to actual methods. Intellij seems to had only pushed import and not the test --- .../org/mockitousage/spies/SpyingOnRealObjectsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java b/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java index 0511965720..06b4878fa3 100644 --- a/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java +++ b/src/test/java/org/mockitousage/spies/SpyingOnRealObjectsTest.java @@ -193,4 +193,13 @@ public void shouldSayNiceMessageWhenSpyingOnPrivateClass() throws Exception { "Most likely it is due to mocking a private class that is not visible to Mockito"); } } + + @Test + public void spysHashCodeEqualsDelegatedToActualMethods() { + List real = new ArrayList<>(); + real.add("one"); + List spy = spy(real); + assertEquals(real.hashCode(), spy.hashCode()); + assertTrue(spy.equals(real)); + } }