From 8f181424fca256accf3ba4b2144da6200966e4a8 Mon Sep 17 00:00:00 2001 From: Tim van der Lippe Date: Fri, 20 Aug 2021 14:53:52 +0100 Subject: [PATCH] Fix verifyNoMoreInteractions inOrder invocations for spies Fixes #2394 --- .../org/mockito/internal/InOrderImpl.java | 20 +++++++++++-- .../SpiesWithRealEqualsAndInOrderTest.java | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/mockitousage/spies/SpiesWithRealEqualsAndInOrderTest.java diff --git a/src/main/java/org/mockito/internal/InOrderImpl.java b/src/main/java/org/mockito/internal/InOrderImpl.java index 7fc0b5f27a..af852e49ae 100644 --- a/src/main/java/org/mockito/internal/InOrderImpl.java +++ b/src/main/java/org/mockito/internal/InOrderImpl.java @@ -6,7 +6,7 @@ import static org.mockito.internal.exceptions.Reporter.inOrderRequiresFamiliarMock; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.mockito.InOrder; @@ -31,7 +31,7 @@ public class InOrderImpl implements InOrder, InOrderContext { private final MockitoCore mockitoCore = new MockitoCore(); - private final List mocksToBeVerifiedInOrder = new LinkedList<>(); + private final List mocksToBeVerifiedInOrder = new ArrayList<>(); private final InOrderContext inOrderContext = new InOrderContextImpl(); public List getMocksToBeVerifiedInOrder() { @@ -56,7 +56,7 @@ public T verify(T mock, VerificationMode mode) { if (!mockingDetails.isMock()) { throw notAMockPassedToVerify(mock.getClass()); } - if (!mocksToBeVerifiedInOrder.contains(mock)) { + if (!this.objectIsMockToBeVerified(mock)) { throw inOrderRequiresFamiliarMock(); } if (mode instanceof VerificationWrapper) { @@ -69,6 +69,20 @@ public T verify(T mock, VerificationMode mode) { return mockitoCore.verify(mock, new InOrderWrapper((VerificationInOrderMode) mode, this)); } + // We can't use `this.mocksToBeVerifiedInOrder.contains`, since that in turn calls `.equals` on + // the mock. Since mocks can be spies and spies get their real equals method calls called, the + // result is that Mockito incorrectly would register an invocation on a mock. This normally + // wouldn't be a problem, unless the user explicitly verifies that no interactions are performed + // on the mock, which would start to fail for the equals invocation. + private boolean objectIsMockToBeVerified(Object mock) { + for (Object inOrderMock : this.mocksToBeVerifiedInOrder) { + if (inOrderMock == mock) { + return true; + } + } + return false; + } + @Override public boolean isVerified(Invocation i) { return inOrderContext.isVerified(i); diff --git a/src/test/java/org/mockitousage/spies/SpiesWithRealEqualsAndInOrderTest.java b/src/test/java/org/mockitousage/spies/SpiesWithRealEqualsAndInOrderTest.java new file mode 100644 index 0000000000..e2dabe9515 --- /dev/null +++ b/src/test/java/org/mockitousage/spies/SpiesWithRealEqualsAndInOrderTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.spies; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.inOrder; + +import org.junit.Test; +import org.mockito.InOrder; + +// https://github.com/mockito/mockito/issues/2394 +public class SpiesWithRealEqualsAndInOrderTest { + + @Test + public void should_be_able_to_handle_in_order_on_spies_with_equals() { + ToBeSpied mock1 = spy(new ToBeSpied()); + ToBeSpied mock2 = spy(new ToBeSpied()); + mock1.someMethod(); + InOrder order = inOrder(mock1, mock2); + order.verify(mock1).someMethod(); + order.verifyNoMoreInteractions(); + } + + static class ToBeSpied { + void someMethod() {} + } +}