Skip to content

Commit

Permalink
Default mock of Optional.isEmpty() returns true when using RETURN_DEE…
Browse files Browse the repository at this point in the history
…P_STUBS

ReturnsDeepStubs now answers with true on Optional.isEmpty()
when using RETURN_DEEP_STUBS.
ReturnsDeepStubs now returns real Optionals instead of mocks.

Fixes mockito#2865
  • Loading branch information
AndreasTu committed Aug 22, 2023
1 parent c637192 commit 4601a88
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 9 deletions.
Expand Up @@ -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);
Expand Down
Expand Up @@ -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) {
Expand All @@ -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;
}
}
@@ -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<String> 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<String> 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<String> 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();
}
}

0 comments on commit 4601a88

Please sign in to comment.