From 4601a8814900d2a44483fb4007e1b8587a3233b1 Mon Sep 17 00:00:00 2001 From: AndreasTu Date: Thu, 17 Aug 2023 21:51:51 +0200 Subject: [PATCH] Default mock of Optional.isEmpty() returns true when using RETURN_DEEP_STUBS ReturnsDeepStubs now answers with true on Optional.isEmpty() when using RETURN_DEEP_STUBS. ReturnsDeepStubs now returns real Optionals instead of mocks. Fixes #2865 --- .../defaultanswers/ReturnsDeepStubs.java | 4 + .../defaultanswers/ReturnsEmptyValues.java | 29 +++- .../ReturnsOptionalMocksTest.java | 154 ++++++++++++++++++ 3 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsOptionalMocksTest.java 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 8356c1b79b..27faed9d9a 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java @@ -54,6 +54,10 @@ public Object answer(InvocationOnMock invocation) throws Throwable { MockCreationSettings mockSettings = MockUtil.getMockSettings(invocation.getMock()); Class rawType = returnTypeGenericMetadata.rawType(); + final var emptyValue = ReturnsEmptyValues.returnCommonEmptyValueFor(rawType); + if (emptyValue != null) { + return emptyValue; + } if (!typeMockabilityOf(rawType, mockSettings.getMockMaker()).mockable()) { if (invocation.getMethod().getReturnType().equals(rawType)) { return delegate().answer(invocation); diff --git a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java index 20e9d85afe..c2dd0f7b2b 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java @@ -139,14 +139,6 @@ Object returnValueFor(Class type) { return new TreeMap<>(); } else if (type == LinkedHashMap.class) { return new LinkedHashMap<>(); - } else if (type == Optional.class) { - return Optional.empty(); - } else if (type == OptionalDouble.class) { - return OptionalDouble.empty(); - } else if (type == OptionalInt.class) { - return OptionalInt.empty(); - } else if (type == OptionalLong.class) { - return OptionalLong.empty(); } else if (type == Stream.class) { return Stream.empty(); } else if (type == DoubleStream.class) { @@ -160,8 +152,27 @@ Object returnValueFor(Class type) { } else if (type == Period.class) { return Period.ZERO; } - // Let's not care about the rest of collections. + + return returnCommonEmptyValueFor(type); + } + + /** + * Returns empty values for common known types, shared between {@link ReturnsEmptyValues} and {@link ReturnsDeepStubs}. + * + * @param type the type to check + * @return the empty value, or {@code null} + */ + static Object returnCommonEmptyValueFor(Class type) { + if (type == Optional.class) { + return Optional.empty(); + } else if (type == OptionalDouble.class) { + return OptionalDouble.empty(); + } else if (type == OptionalInt.class) { + return OptionalInt.empty(); + } else if (type == OptionalLong.class) { + return OptionalLong.empty(); + } return null; } } diff --git a/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsOptionalMocksTest.java b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsOptionalMocksTest.java new file mode 100644 index 0000000000..3364b6d984 --- /dev/null +++ b/src/test/java/org/mockito/internal/stubbing/defaultanswers/ReturnsOptionalMocksTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2023 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.stubbing.defaultanswers; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockingDetails; + +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; + +import org.junit.Test; + +public class ReturnsOptionalMocksTest { + private interface Type { + Optional getOptString(); + + OptionalLong getOptLong(); + + OptionalDouble getOptDouble(); + + OptionalInt getOptInt(); + } + + @Test + public void deepStubs_Optional_should_return_normal_optional_empty_Issue2865() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + assertThat(type.getOptString()).isEqualTo(Optional.empty()); + assertIsNoMock(type.getOptString()); + } + + @Test + public void deepStubs_OptionalLong_should_return_normal_optional_empty() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + assertThat(type.getOptLong()).isEqualTo(OptionalLong.empty()); + assertIsNoMock(type.getOptLong()); + } + + @Test + public void deepStubs_OptionalDouble_should_normal_optional_empty() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + assertThat(type.getOptDouble()).isEqualTo(OptionalDouble.empty()); + assertIsNoMock(type.getOptDouble()); + } + + @Test + public void deepStubs_OptionalInt_should_normal_optional_empty() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + assertThat(type.getOptInt()).isEqualTo(OptionalInt.empty()); + assertIsNoMock(type.getOptInt()); + } + + @Test + public void normal_mock_Optional_should_return_normal_optional_empty() { + final Type type = mock(Type.class); + assertThat(type.getOptString()).isEqualTo(Optional.empty()); + assertIsNoMock(type.getOptString()); + } + + @Test + public void normal_mock_OptionalLong_should_return_normal_optional_empty() { + final Type type = mock(Type.class); + assertThat(type.getOptLong()).isEqualTo(OptionalLong.empty()); + } + + @Test + public void normal_mock_OptionalDouble_should_return_normal_optional_empty() { + final Type type = mock(Type.class); + assertThat(type.getOptDouble()).isEqualTo(OptionalDouble.empty()); + } + + @Test + public void normal_mock_OptionalInt_should_return_normal_optional_empty() { + final Type type = mock(Type.class); + assertThat(type.getOptInt()).isEqualTo(OptionalInt.empty()); + } + + @Test + public void deepStubs_Optional_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + Optional opt = type.getOptString(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void deepStubs_OptionalLong_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + OptionalLong opt = type.getOptLong(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void deepStubs_OptionalDouble_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + OptionalDouble opt = type.getOptDouble(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void deepStubs_OptionalInt_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class, RETURNS_DEEP_STUBS); + OptionalInt opt = type.getOptInt(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void normal_mock_Optional_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class); + + Optional opt = type.getOptString(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void normal_mock_OptionalLong_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class); + + OptionalLong opt = type.getOptLong(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void normal_mock_OptionalDouble_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class); + + OptionalDouble opt = type.getOptDouble(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + @Test + public void normal_mock_OptionalInt_isPresent_isEmpty_Issue2865() { + final Type type = mock(Type.class); + + OptionalInt opt = type.getOptInt(); + assertThat(opt.isPresent()).isEqualTo(false); + assertThat(opt.isEmpty()).isEqualTo(true); + } + + private void assertIsNoMock(Object mock) { + assertThat(mockingDetails(mock).isMock()).isFalse(); + } +}