Skip to content

Commit

Permalink
Restore behavior for mocks configured without strictness (#2659)
Browse files Browse the repository at this point in the history
Fixes #2656
  • Loading branch information
big-andy-coates committed Jun 2, 2022
1 parent 1fbef57 commit ff98622
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 22 deletions.
35 changes: 30 additions & 5 deletions src/main/java/org/mockito/Mock.java
Expand Up @@ -13,7 +13,6 @@
import java.lang.annotation.Target;

import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;

/**
Expand All @@ -34,12 +33,13 @@
* @Mock(name = "database") private ArticleDatabase dbMock;
* @Mock(answer = RETURNS_MOCKS) private UserProvider userProvider;
* @Mock(extraInterfaces = {Queue.class, Observer.class}) private ArticleMonitor articleMonitor;
* @Mock(strictness = Mock.Strictness.LENIENT) private ArticleConsumer articleConsumer;
* @Mock(stubOnly = true) private Logger logger;
*
* private ArticleManager manager;
*
* @Before public void setup() {
* manager = new ArticleManager(userProvider, database, calculator, articleMonitor, logger);
* manager = new ArticleManager(userProvider, database, calculator, articleMonitor, articleConsumer, logger);
* }
* }
*
Expand Down Expand Up @@ -117,10 +117,35 @@
boolean lenient() default false;

/**
* Mock will have custom strictness, see {@link MockSettings#strictness(Strictness)}.
* Mock will have custom strictness, see {@link MockSettings#strictness(org.mockito.quality.Strictness)}.
* For examples how to use 'Mock' annotation and parameters see {@link Mock}.
*
* @since 4.6.0
* @since 4.6.1
*/
Strictness strictness() default Strictness.STRICT_STUBS;
Strictness strictness() default Strictness.TEST_LEVEL_DEFAULT;

enum Strictness {

/**
* Default value used to indicate the mock does not override the test level strictness.
*
* @since 4.6.1
*/
TEST_LEVEL_DEFAULT,

/**
* See {@link org.mockito.quality.Strictness#LENIENT}
*/
LENIENT,

/**
* See {@link org.mockito.quality.Strictness#WARN}
*/
WARN,

/**
* See {@link org.mockito.quality.Strictness#STRICT_STUBS}
*/
STRICT_STUBS
}
}
Expand Up @@ -17,6 +17,7 @@
import org.mockito.Mockito;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.internal.util.Supplier;
import org.mockito.quality.Strictness;

/**
* Instantiates a mock on a field annotated by {@link Mock}
Expand All @@ -28,6 +29,7 @@ public Object process(Mock annotation, Field field) {
annotation, field.getType(), field::getGenericType, field.getName());
}

@SuppressWarnings("deprecation")
public static Object processAnnotationForMock(
Mock annotation, Class<?> type, Supplier<Type> genericType, String name) {
MockSettings mockSettings = Mockito.withSettings();
Expand All @@ -45,10 +47,12 @@ public static Object processAnnotationForMock(
if (annotation.stubOnly()) {
mockSettings.stubOnly();
}
mockSettings.strictness(annotation.strictness());
if (annotation.lenient()) {
mockSettings.lenient();
}
if (annotation.strictness() != Mock.Strictness.TEST_LEVEL_DEFAULT) {
mockSettings.strictness(Strictness.valueOf(annotation.strictness().toString()));
}

// see @Mock answer default value
mockSettings.defaultAnswer(annotation.answer());
Expand Down
Expand Up @@ -247,10 +247,10 @@ public MockSettings lenient() {

@Override
public MockSettings strictness(Strictness strictness) {
this.strictness = strictness;
if (strictness == null) {
throw strictnessDoesNotAcceptNullParameter();
}
this.strictness = strictness;
return this;
}

Expand Down
Expand Up @@ -45,7 +45,7 @@ public class CreationSettings<T> implements MockCreationSettings<T>, Serializabl
private boolean useConstructor;
private Object outerClassInstance;
private Object[] constructorArgs;
protected Strictness strictness = Strictness.STRICT_STUBS;
protected Strictness strictness = null;

public CreationSettings() {}

Expand Down
58 changes: 58 additions & 0 deletions src/test/java/org/mockito/MockTest.java
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2022 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito;

import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import static org.hamcrest.Matchers.not;
import static org.junit.Assume.assumeThat;

@RunWith(Enclosed.class)
public class MockTest {

@RunWith(value = Parameterized.class)
public static class StrictnessToMockStrictnessTest {

public org.mockito.quality.Strictness strictness;

public StrictnessToMockStrictnessTest(org.mockito.quality.Strictness strictness) {
this.strictness = strictness;
}

@Test
public void should_have_matching_enum_in_mock_strictness_enum() {
Mock.Strictness.valueOf(strictness.name());
}

@Parameterized.Parameters(name = "{0}")
public static org.mockito.quality.Strictness[] data() {
return org.mockito.quality.Strictness.values();
}
}

@RunWith(value = Parameterized.class)
public static class MockStrictnessToStrictnessTest {

public Mock.Strictness strictness;

public MockStrictnessToStrictnessTest(Mock.Strictness strictness) {
this.strictness = strictness;
}

@Test
public void should_have_matching_enum_in_strictness_enum() {
assumeThat("Ignore NOT_SET", strictness, not(Mock.Strictness.TEST_LEVEL_DEFAULT));
org.mockito.quality.Strictness.valueOf(strictness.name());
}

@Parameterized.Parameters(name = "{0}")
public static Mock.Strictness[] data() {
return Mock.Strictness.values();
}
}
}
Expand Up @@ -7,6 +7,8 @@
import org.assertj.core.api.Assertions;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.exceptions.misusing.PotentialStubbingProblem;
import org.mockito.junit.MockitoJUnit;
Expand All @@ -16,28 +18,56 @@

import static org.mockito.Mockito.when;

@RunWith(Enclosed.class)
public class StrictnessMockAnnotationTest {

public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
public static class StrictStubsTest {
public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);

@Mock(strictness = Strictness.LENIENT)
IMethods lenientMock;
@Mock(strictness = Mock.Strictness.LENIENT)
IMethods lenientMock;

@Mock IMethods regularMock;
@Mock IMethods regularMock;

@Test
public void mock_is_lenient() {
when(lenientMock.simpleMethod("1")).thenReturn("1");
@Test
public void mock_is_lenient() {
when(lenientMock.simpleMethod("1")).thenReturn("1");

// then lenient mock does not throw:
ProductionCode.simpleMethod(lenientMock, "3");
// then lenient mock does not throw:
ProductionCode.simpleMethod(lenientMock, "3");
}

@Test
public void mock_is_strict() {
when(regularMock.simpleMethod("2")).thenReturn("2");

Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4"))
.isInstanceOf(PotentialStubbingProblem.class);
}
}

@Test
public void mock_is_strict() {
when(regularMock.simpleMethod("2")).thenReturn("2");
public static class LenientStubsTest {
public @Rule MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT);

@Mock IMethods lenientMock;

@Mock(strictness = Mock.Strictness.STRICT_STUBS)
IMethods regularMock;

@Test
public void mock_is_lenient() {
when(lenientMock.simpleMethod("1")).thenReturn("1");

// then lenient mock does not throw:
ProductionCode.simpleMethod(lenientMock, "3");
}

@Test
public void mock_is_strict() {
when(regularMock.simpleMethod("2")).thenReturn("2");

Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4"))
.isInstanceOf(PotentialStubbingProblem.class);
Assertions.assertThatThrownBy(() -> ProductionCode.simpleMethod(regularMock, "4"))
.isInstanceOf(PotentialStubbingProblem.class);
}
}
}
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2022 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockitousage;

import java.util.function.Predicate;

public class ProductionCode {

@SuppressWarnings("ReturnValueIgnored")
public static void simpleMethod(Predicate<String> mock, String argument) {
mock.test(argument);
}
}
Expand Up @@ -21,7 +21,9 @@
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
Expand Down Expand Up @@ -162,6 +164,29 @@ void inherits_strictness_from_base_class() {
assertThat(result.getStatus()).isEqualTo(TestExecutionResult.Status.SUCCESSFUL);
}

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
static class LenientMockitoSettings {

@Mock
private Predicate<String> rootMock;

@Test
void should_not_throw_on_potential_stubbing_issue() {
Mockito.doReturn(true).when(rootMock).test("Foo");

ProductionCode.simpleMethod(rootMock, "Bar");
}
}

@Test
void use_strictness_from_settings_annotation() {
TestExecutionResult result = invokeTestClassAndRetrieveMethodResult(LenientMockitoSettings.class);

assertThat(result.getThrowable()).isEqualTo(Optional.empty());
assertThat(result.getStatus()).isEqualTo(TestExecutionResult.Status.SUCCESSFUL);
}

private TestExecutionResult invokeTestClassAndRetrieveMethodResult(Class<?> clazz) {
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(
Expand Down

0 comments on commit ff98622

Please sign in to comment.