Skip to content

Commit

Permalink
Fix mockito#2915 Forbid spy on mocked interface
Browse files Browse the repository at this point in the history
  • Loading branch information
bmaggi committed Apr 3, 2023
1 parent f9fbf50 commit a09e35f
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/main/java/org/mockito/Mockito.java
Expand Up @@ -10,6 +10,7 @@
import org.mockito.internal.creation.MockSettingsImpl;
import org.mockito.internal.framework.DefaultMockitoFramework;
import org.mockito.internal.session.DefaultMockitoSessionBuilder;
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.verification.VerificationModeFactory;
import org.mockito.invocation.Invocation;
import org.mockito.invocation.InvocationFactory;
Expand Down Expand Up @@ -2167,6 +2168,10 @@ public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
* @return a spy of the real object
*/
public static <T> T spy(T object) {
if (MockUtil.isMockOnInterface(object)) {
throw new IllegalArgumentException(
"Please don't pass interface here. Spy is not allowed on mocked interface.");
}
return MOCKITO_CORE.mock(
(Class<T>) object.getClass(),
withSettings().spiedInstance(object).defaultAnswer(CALLS_REAL_METHODS));
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/mockito/internal/util/MockUtil.java
Expand Up @@ -147,6 +147,17 @@ public static boolean isMock(Object mock) {
return getMockHandlerOrNull(mock) != null;
}

public static boolean isMockOnInterface(Object mock) {
if (mock == null) {
return false;
}
MockHandler<?> mockHandlerOrNull = getMockHandlerOrNull(mock);
if (mockHandlerOrNull == null){
return false;
}
return mockHandlerOrNull.getMockSettings().getTypeToMock().isInterface();
}

private static MockHandler<?> getMockHandlerOrNull(Object mock) {
if (mock == null) {
throw new NotAMockException("Argument should be a mock, but is null!");
Expand Down
9 changes: 9 additions & 0 deletions src/test/java/org/mockito/internal/util/MockUtilTest.java
Expand Up @@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.withSettings;

import java.util.AbstractSequentialList;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -61,6 +62,14 @@ public void should_validate_mock() {
assertTrue(MockUtil.isMock(Mockito.mock(List.class)));
}

@Test
public void should_validate_mock_on_interface() {
assertFalse(MockUtil.isMockOnInterface("i mock a mock"));
assertFalse(MockUtil.isMockOnInterface(Mockito.mock(ArrayList.class)));
assertFalse(MockUtil.isMockOnInterface(Mockito.mock(AbstractSequentialList.class)));
assertTrue(MockUtil.isMockOnInterface(Mockito.mock(List.class)));
}

@Test
public void should_validate_spy() {
assertFalse(MockUtil.isSpy("i mock a mock"));
Expand Down
Expand Up @@ -409,6 +409,19 @@ public void handles_bridge_methods_correctly() {
assertEquals("value", testBug.getValue(0));
}


@Test
public void forbid_spy_on_mocked_interface() {
try {
spy(mock(List.class));
fail();
} catch (IllegalArgumentException e) {
assertThat(e)
.hasMessageContaining(
"Please don't pass interface here. Spy is not allowed on mocked interface.");
}
}

public abstract class SomeAbstractClass<T> {

protected abstract String getRealValue(T value);
Expand Down

0 comments on commit a09e35f

Please sign in to comment.