diff --git a/src/main/java/org/mockito/Mock.java b/src/main/java/org/mockito/Mock.java index 7c3e036083..e8469b830d 100644 --- a/src/main/java/org/mockito/Mock.java +++ b/src/main/java/org/mockito/Mock.java @@ -13,7 +13,6 @@ import java.lang.annotation.Target; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; /** @@ -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); * } * } * @@ -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 + } } diff --git a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java index 484c9102e4..66c3f31d12 100644 --- a/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java +++ b/src/main/java/org/mockito/internal/configuration/MockAnnotationProcessor.java @@ -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} @@ -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 genericType, String name) { MockSettings mockSettings = Mockito.withSettings(); @@ -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()); diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index e1dcd48f9e..f73a718298 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -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; } diff --git a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java index 50bb937696..13939f6fbd 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -45,7 +45,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; - protected Strictness strictness = Strictness.STRICT_STUBS; + protected Strictness strictness = null; public CreationSettings() {} diff --git a/src/test/java/org/mockito/MockTest.java b/src/test/java/org/mockito/MockTest.java new file mode 100644 index 0000000000..e904ec5d45 --- /dev/null +++ b/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(); + } + } +} diff --git a/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java index c035b3d708..7250555880 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessMockAnnotationTest.java @@ -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; @@ -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); + } } } diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java new file mode 100644 index 0000000000..f2953c7bc6 --- /dev/null +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/ProductionCode.java @@ -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 mock, String argument) { + mock.test(argument); + } +} diff --git a/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java b/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java index 31a5d6a649..1b63d66cc4 100644 --- a/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java +++ b/subprojects/junit-jupiter/src/test/java/org/mockitousage/StrictnessTest.java @@ -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; @@ -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 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(