From 18456b70c4c97dd75322f749eb7eb2fe2c0e9ec0 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sun, 10 Dec 2017 16:12:55 -0800 Subject: [PATCH 01/50] Experimenting with Strictness per mock --- src/main/java/org/mockito/MockSettings.java | 3 ++ .../internal/creation/MockSettingsImpl.java | 7 +++ .../creation/settings/CreationSettings.java | 8 ++++ .../internal/handler/MockHandlerImpl.java | 9 ++-- .../junit/DefaultStubbingLookupListener.java | 10 ++++- .../internal/junit/UnusedStubbings.java | 24 ++++++---- .../listeners/StubbingLookupListener.java | 7 +-- .../stubbing/InvocationContainerImpl.java | 1 + .../internal/stubbing/StubbingStrictness.java | 17 +++++++ .../mockito/mock/MockCreationSettings.java | 3 ++ .../stubbing/StrictnessPerMockTest.java | 44 +++++++++++++++++++ 11 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java create mode 100644 src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index 82dc0500a5..d8753fa79a 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -10,6 +10,7 @@ import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -316,4 +317,6 @@ public interface MockSettings extends Serializable { */ @Incubating MockCreationSettings build(Class typeToMock); + + MockSettings strictness(Strictness strictness); } diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index 82af98aeb8..c28599d3a4 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -15,6 +15,7 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -226,6 +227,12 @@ public MockCreationSettings build(Class typeToMock) { return validatedSettings(typeToMock, (CreationSettings) this); } + @Override + public MockSettings strictness(Strictness strictness) { + this.strictness = strictness; + return this; + } + private static CreationSettings validatedSettings(Class typeToMock, CreationSettings source) { MockCreationValidator validator = new MockCreationValidator(); 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 4befa99824..1d01f1a123 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -10,6 +10,7 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -37,6 +38,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; + protected Strictness strictness; public CreationSettings() {} @@ -55,6 +57,7 @@ public CreationSettings(CreationSettings copy) { this.useConstructor = copy.isUsingConstructor(); this.outerClassInstance = copy.getOuterClassInstance(); this.constructorArgs = copy.getConstructorArgs(); + this.strictness = copy.strictness; } @Override @@ -153,4 +156,9 @@ public Object getOuterClassInstance() { public boolean isStubOnly() { return stubOnly; } + + @Override + public Strictness getStrictness() { + return strictness; + } } diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 57a9d435b0..44b8639a35 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -16,6 +16,7 @@ import org.mockito.internal.verification.VerificationDataImpl; import org.mockito.invocation.Invocation; import org.mockito.invocation.InvocationContainer; +import org.mockito.invocation.MatchableInvocation; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; import org.mockito.verification.VerificationMode; @@ -88,7 +89,7 @@ public Object handle(Invocation invocation) throws Throwable { // look for existing answer for this invocation StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation); - notifyStubbedAnswerLookup(invocation, stubbing); + notifyStubbedAnswerLookup(invocation, stubbing, mockSettings); if (stubbing != null) { stubbing.captureArgumentsFrom(invocation); @@ -130,11 +131,11 @@ private VerificationDataImpl createVerificationData(InvocationContainerImpl invo return new VerificationDataImpl(invocationContainer, invocationMatcher); } - private void notifyStubbedAnswerLookup(Invocation invocation, StubbedInvocationMatcher exception) { + private void notifyStubbedAnswerLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings) { //TODO #793 - when completed, we should be able to get rid of the casting below - List listeners = ((CreationSettings) mockSettings).getStubbingLookupListeners(); + List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { - listener.onStubbingLookup(invocation, exception); + listener.onStubbingLookup(invocation, stubbingFound, mockSettings); } } } diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index c7e2551e90..23299015c3 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -8,6 +8,7 @@ import org.mockito.internal.listeners.StubbingLookupListener; import org.mockito.invocation.Invocation; import org.mockito.invocation.MatchableInvocation; +import org.mockito.mock.MockCreationSettings; import org.mockito.quality.Strictness; import org.mockito.stubbing.Stubbing; @@ -30,11 +31,18 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { this.currentStrictness = strictness; } - public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound) { + public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings) { + //TODO this is not quite right if (currentStrictness != Strictness.STRICT_STUBS) { return; } + if (mockSettings.getStrictness() == Strictness.LENIENT || mockSettings.getStrictness() == Strictness.WARN) { + //strictness explicitly relaxed at the mock level + return; + } + + if (stubbingFound == null) { //If stubbing was not found for invocation it means that either the mock invocation was not stubbed or //we have a stubbing arg mismatch. diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbings.java b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java index 5b6434d840..d262dccc9c 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbings.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java @@ -5,12 +5,13 @@ package org.mockito.internal.junit; import org.mockito.internal.exceptions.Reporter; +import org.mockito.internal.stubbing.StubbingStrictness; import org.mockito.internal.util.MockitoLogger; -import org.mockito.internal.util.collections.ListUtil; import org.mockito.invocation.Invocation; import org.mockito.stubbing.Stubbing; import java.util.Collection; +import java.util.LinkedList; import java.util.List; /** @@ -48,15 +49,20 @@ public String toString() { } public void reportUnused() { - if (!unused.isEmpty()) { - List invocations = ListUtil.convert(unused, (ListUtil.Converter) new ListUtil.Converter() { - public Invocation convert(Stubbing s) { - return s.getInvocation(); - } - }); - + if (unused.isEmpty()) { + return; + } - Reporter.unncessaryStubbingException(invocations); + List invocations = new LinkedList(); + for (Stubbing stubbing : unused) { + if (!StubbingStrictness.isLenientStubbing(stubbing)) { + invocations.add(stubbing.getInvocation()); + } + } + if (invocations.isEmpty()) { + return; } + + Reporter.unncessaryStubbingException(invocations); } } diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java index 81cf537776..9144bb4482 100644 --- a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java @@ -6,6 +6,7 @@ import org.mockito.invocation.Invocation; import org.mockito.invocation.MatchableInvocation; +import org.mockito.mock.MockCreationSettings; /** * Listens to attempts to look up stubbing answer for given mocks. This class is internal for now. @@ -28,10 +29,10 @@ public interface StubbingLookupListener { * Called by the framework when Mockito looked up an answer for invocation on a mock. * * TODO when making this public, we should have an event object instead of 2 arguments in the listener. - * - * @param invocation the invocation on the mock + * @param invocation the invocation on the mock * @param stubbingFound - can be null - it indicates that the invocation was not stubbed. + * @param mockSettings */ - void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound); + void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings); } diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 120320be1d..359152fdb2 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -84,6 +84,7 @@ public StubbedInvocationMatcher findAnswerFor(Invocation invocation) { for (StubbedInvocationMatcher s : stubbed) { if (s.matches(invocation)) { s.markStubUsed(invocation); + //TODO we should mark stubbed at the point of stubbing, not at the point where the stub is being used invocation.markStubbed(new StubInfoImpl(s)); return s; } diff --git a/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java b/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java new file mode 100644 index 0000000000..4878a331b3 --- /dev/null +++ b/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.stubbing; + +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Stubbing; + +import static org.mockito.Mockito.mockingDetails; + +public class StubbingStrictness { + public static boolean isLenientStubbing(Stubbing stubbing) { + Strictness mockStrictness = mockingDetails(stubbing.getInvocation().getMock()).getMockCreationSettings().getStrictness(); + return mockStrictness == Strictness.LENIENT; + } +} diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index b58daf0efa..d555143c67 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -9,6 +9,7 @@ import org.mockito.NotExtensible; import org.mockito.listeners.InvocationListener; import org.mockito.listeners.VerificationStartedListener; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.List; @@ -109,4 +110,6 @@ public interface MockCreationSettings { */ @Incubating Object getOuterClassInstance(); + + Strictness getStrictness(); } diff --git a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java new file mode 100644 index 0000000000..aa4c3e6298 --- /dev/null +++ b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitousage.stubbing; + +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; + +import static org.junit.Assert.fail; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.withSettings; + +public class StrictnessPerMockTest { + + MockitoSession mockito = Mockito.mockitoSession().strictness(Strictness.STRICT_STUBS).startMocking(); + + @Test public void strictness_per_mock() throws Throwable { + //given + IMethods mock = mock(IMethods.class); + IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); + + //when + given(lenientMock.simpleMethod(100)).willReturn("100"); + given(mock.simpleMethod(100)).willReturn("100"); + + //on lenient mock, we can call the stubbed method with different argument: + lenientMock.simpleMethod(200); + + //on other mock, we will get strict stubbing exception + try { + mock.simpleMethod(200); + fail(); + } catch (PotentialStubbingProblem e) {} + + //we are not reporting unused stubbings here: + mockito.finishMocking(); + } +} From ab2e7eb1a62fb9360aa3c8ade41bde564a17cc05 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 29 Dec 2017 07:23:19 -1000 Subject: [PATCH 02/50] Progress on strictness per mock/stubbing Implemented basic support for setting strictness per stubbing. Refactorings and more test coverage is pending. Some features not supported yet. --- src/main/java/org/mockito/Mockito.java | 5 + .../org/mockito/internal/MockitoCore.java | 14 +++ .../internal/handler/MockHandlerImpl.java | 4 +- .../junit/DefaultStubbingLookupListener.java | 7 +- .../listeners/StubbingLookupListener.java | 4 +- .../stubbing/DefaultLenientStubber.java | 64 ++++++++++ .../stubbing/InvocationContainerImpl.java | 13 +- .../stubbing/OngoingStubbingImpl.java | 13 +- .../stubbing/StubbedInvocationMatcher.java | 14 ++- .../defaultanswers/ReturnsDeepStubs.java | 2 +- .../org/mockito/stubbing/BaseStubber.java | 117 ++++++++++++++++++ .../org/mockito/stubbing/LenientStubber.java | 10 ++ .../java/org/mockito/stubbing/Stubber.java | 109 +--------------- .../java/org/mockito/stubbing/Stubbing.java | 3 + .../internal/junit/UnusedStubbingsTest.java | 10 +- .../InvocationContainerImplStubbingTest.java | 14 +-- .../stubbing/InvocationContainerImplTest.java | 19 +-- .../stubbing/StrictnessPerMockTest.java | 52 ++++++-- 18 files changed, 315 insertions(+), 159 deletions(-) create mode 100644 src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java create mode 100644 src/main/java/org/mockito/stubbing/BaseStubber.java create mode 100644 src/main/java/org/mockito/stubbing/LenientStubber.java diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 2a384885e2..f503070839 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -28,6 +28,7 @@ import org.mockito.session.MockitoSessionLogger; import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer1; +import org.mockito.stubbing.LenientStubber; import org.mockito.stubbing.OngoingStubbing; import org.mockito.stubbing.Stubber; import org.mockito.stubbing.Stubbing; @@ -2950,4 +2951,8 @@ public static MockitoFramework framework() { public static MockitoSessionBuilder mockitoSession() { return new DefaultMockitoSessionBuilder(); } + + public static LenientStubber lenient() { + return MOCKITO_CORE.lenient(); + } } diff --git a/src/main/java/org/mockito/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java index 419d228436..28809b55e9 100644 --- a/src/main/java/org/mockito/internal/MockitoCore.java +++ b/src/main/java/org/mockito/internal/MockitoCore.java @@ -12,6 +12,7 @@ import org.mockito.internal.invocation.finder.VerifiableInvocationsFinder; import org.mockito.internal.listeners.VerificationStartedNotifier; import org.mockito.internal.progress.MockingProgress; +import org.mockito.internal.stubbing.DefaultLenientStubber; import org.mockito.internal.stubbing.InvocationContainerImpl; import org.mockito.internal.stubbing.OngoingStubbingImpl; import org.mockito.internal.stubbing.StubberImpl; @@ -25,6 +26,7 @@ import org.mockito.invocation.Invocation; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; +import org.mockito.stubbing.LenientStubber; import org.mockito.stubbing.OngoingStubbing; import org.mockito.stubbing.Stubber; import org.mockito.verification.VerificationMode; @@ -165,6 +167,14 @@ public InOrder inOrder(Object... mocks) { } public Stubber stubber() { + return createStubber(false); + } + + public Stubber lenientStubber() { + return createStubber(true); + } + + private Stubber createStubber(boolean lenient) { MockingProgress mockingProgress = mockingProgress(); mockingProgress.stubbingStarted(); mockingProgress.resetOngoingStubbing(); @@ -202,4 +212,8 @@ public Object[] ignoreStubs(Object... mocks) { public MockingDetails mockingDetails(Object toInspect) { return new DefaultMockingDetails(toInspect); } + + public LenientStubber lenient() { + return new DefaultLenientStubber(); + } } diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 44b8639a35..67fe948b03 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -16,9 +16,9 @@ import org.mockito.internal.verification.VerificationDataImpl; import org.mockito.invocation.Invocation; import org.mockito.invocation.InvocationContainer; -import org.mockito.invocation.MatchableInvocation; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; +import org.mockito.stubbing.Stubbing; import org.mockito.verification.VerificationMode; import java.util.List; @@ -131,7 +131,7 @@ private VerificationDataImpl createVerificationData(InvocationContainerImpl invo return new VerificationDataImpl(invocationContainer, invocationMatcher); } - private void notifyStubbedAnswerLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings) { + private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { //TODO #793 - when completed, we should be able to get rid of the casting below List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 23299015c3..87cc5bd226 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -7,7 +7,6 @@ import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.listeners.StubbingLookupListener; import org.mockito.invocation.Invocation; -import org.mockito.invocation.MatchableInvocation; import org.mockito.mock.MockCreationSettings; import org.mockito.quality.Strictness; import org.mockito.stubbing.Stubbing; @@ -31,7 +30,7 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { this.currentStrictness = strictness; } - public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings) { + public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { //TODO this is not quite right if (currentStrictness != Strictness.STRICT_STUBS) { return; @@ -42,7 +41,6 @@ public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbing return; } - if (stubbingFound == null) { //If stubbing was not found for invocation it means that either the mock invocation was not stubbed or //we have a stubbing arg mismatch. @@ -62,7 +60,8 @@ private static List potentialArgMismatches(Invocation invocation) { List matchingStubbings = new LinkedList(); Collection stubbings = mockingDetails(invocation.getMock()).getStubbings(); for (Stubbing s : stubbings) { - if (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName())) { + if (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName()) + && s.getStrictness() != Strictness.LENIENT) { matchingStubbings.add(s.getInvocation()); } } diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java index 9144bb4482..e553168970 100644 --- a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java @@ -5,8 +5,8 @@ package org.mockito.internal.listeners; import org.mockito.invocation.Invocation; -import org.mockito.invocation.MatchableInvocation; import org.mockito.mock.MockCreationSettings; +import org.mockito.stubbing.Stubbing; /** * Listens to attempts to look up stubbing answer for given mocks. This class is internal for now. @@ -33,6 +33,6 @@ public interface StubbingLookupListener { * @param stubbingFound - can be null - it indicates that the invocation was not stubbed. * @param mockSettings */ - void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound, MockCreationSettings mockSettings); + void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings); } diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java new file mode 100644 index 0000000000..65c107d804 --- /dev/null +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.stubbing; + +import org.mockito.internal.MockitoCore; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; +import org.mockito.stubbing.LenientStubber; +import org.mockito.stubbing.OngoingStubbing; +import org.mockito.stubbing.Stubber; + +public class DefaultLenientStubber implements LenientStubber { + + private final static MockitoCore MOCKITO_CORE = new MockitoCore(); + + @Override + public Stubber doThrow(Throwable... toBeThrown) { + return null; + } + + @Override + public Stubber doThrow(Class toBeThrown) { + return null; + } + + @Override + public Stubber doThrow(Class toBeThrown, Class... nextToBeThrown) { + return null; + } + + @Override + public Stubber doAnswer(Answer answer) { + return null; + } + + @Override + public Stubber doNothing() { + return null; + } + + @Override + public Stubber doReturn(Object toBeReturned) { + return null; + } + + @Override + public Stubber doReturn(Object toBeReturned, Object... nextToBeReturned) { + return null; + } + + @Override + public Stubber doCallRealMethod() { + return null; + } + + @Override + public OngoingStubbing when(T methodCall) { + OngoingStubbingImpl ongoingStubbing = (OngoingStubbingImpl) MOCKITO_CORE.when(methodCall); + ongoingStubbing.setStrictness(Strictness.LENIENT); + return ongoingStubbing; + } +} diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 359152fdb2..4c66e74f8f 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -12,6 +12,7 @@ import org.mockito.invocation.InvocationContainer; import org.mockito.invocation.MatchableInvocation; import org.mockito.mock.MockCreationSettings; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import org.mockito.stubbing.Stubbing; import org.mockito.stubbing.ValidableAnswer; @@ -46,19 +47,19 @@ public void resetInvocationForPotentialStubbing(MatchableInvocation invocationMa this.invocationForStubbing = invocationMatcher; } - public void addAnswer(Answer answer) { + public void addAnswer(Answer answer, Strictness strictness) { registeredInvocations.removeLast(); - addAnswer(answer, false); + addAnswer(answer, false, strictness); } public void addConsecutiveAnswer(Answer answer) { - addAnswer(answer, true); + addAnswer(answer, true, null); } /** * Adds new stubbed answer and returns the invocation matcher the answer was added to. */ - public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive) { + public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness strictness) { Invocation invocation = invocationForStubbing.getInvocation(); mockingProgress().stubbingCompleted(); if (answer instanceof ValidableAnswer) { @@ -69,7 +70,7 @@ public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive) if (isConsecutive) { stubbed.getFirst().addAnswer(answer); } else { - stubbed.addFirst(new StubbedInvocationMatcher(invocationForStubbing, answer)); + stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, strictness)); } return stubbed.getFirst(); } @@ -110,7 +111,7 @@ public void setMethodForStubbing(MatchableInvocation invocation) { invocationForStubbing = invocation; assert hasAnswersForStubbing(); for (int i = 0; i < answersForStubbing.size(); i++) { - addAnswer(answersForStubbing.get(i), i != 0); + addAnswer(answersForStubbing.get(i), i != 0, null); } answersForStubbing.clear(); } diff --git a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java index 78a10270a3..ad95d90882 100644 --- a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java @@ -5,16 +5,18 @@ package org.mockito.internal.stubbing; import org.mockito.invocation.Invocation; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import org.mockito.stubbing.OngoingStubbing; -import static org.mockito.internal.exceptions.Reporter.incorrectUseOfApi; - import java.util.List; +import static org.mockito.internal.exceptions.Reporter.incorrectUseOfApi; + public class OngoingStubbingImpl extends BaseStubbing { private final InvocationContainerImpl invocationContainer; + private Strictness strictness; public OngoingStubbingImpl(InvocationContainerImpl invocationContainer) { this.invocationContainer = invocationContainer; @@ -22,11 +24,12 @@ public OngoingStubbingImpl(InvocationContainerImpl invocationContainer) { @Override public OngoingStubbing thenAnswer(Answer answer) { + //TODO SF! rename all iOngoingStubbing -> ongoingStubbing if(!invocationContainer.hasInvocationForPotentialStubbing()) { throw incorrectUseOfApi(); } - invocationContainer.addAnswer(answer); + invocationContainer.addAnswer(answer, strictness); return new ConsecutiveStubbing(invocationContainer); } @@ -45,6 +48,10 @@ public List getRegisteredInvocations() { public M getMock() { return (M) invocationContainer.invokedMock(); } + + public void setStrictness(Strictness strictness) { + this.strictness = strictness; + } } diff --git a/src/main/java/org/mockito/internal/stubbing/StubbedInvocationMatcher.java b/src/main/java/org/mockito/internal/stubbing/StubbedInvocationMatcher.java index 45182105c2..e7ccac4d11 100644 --- a/src/main/java/org/mockito/internal/stubbing/StubbedInvocationMatcher.java +++ b/src/main/java/org/mockito/internal/stubbing/StubbedInvocationMatcher.java @@ -5,11 +5,12 @@ package org.mockito.internal.stubbing; import org.mockito.internal.invocation.InvocationMatcher; -import org.mockito.invocation.MatchableInvocation; -import org.mockito.stubbing.Stubbing; import org.mockito.invocation.DescribedInvocation; import org.mockito.invocation.InvocationOnMock; +import org.mockito.invocation.MatchableInvocation; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; +import org.mockito.stubbing.Stubbing; import java.io.Serializable; import java.util.Queue; @@ -20,10 +21,12 @@ public class StubbedInvocationMatcher extends InvocationMatcher implements Seria private static final long serialVersionUID = 4919105134123672727L; private final Queue answers = new ConcurrentLinkedQueue(); + private final Strictness strictness; private DescribedInvocation usedAt; - public StubbedInvocationMatcher(MatchableInvocation invocation, Answer answer) { + public StubbedInvocationMatcher(Answer answer, MatchableInvocation invocation, Strictness strictness) { super(invocation.getInvocation(), invocation.getMatchers()); + this.strictness = strictness; this.answers.add(answer); } @@ -52,4 +55,9 @@ public boolean wasUsed() { public String toString() { return super.toString() + " stubbed with: " + answers; } + + @Override + public Strictness getStrictness() { + return strictness; + } } 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 3b2cb3dabf..901c0a6810 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java @@ -118,7 +118,7 @@ private ReturnsDeepStubs returnsDeepStubsAnswerUsing(final GenericMetadataSuppor private StubbedInvocationMatcher recordDeepStubAnswer(final Object mock, InvocationContainerImpl container) { DeeplyStubbedAnswer answer = new DeeplyStubbedAnswer(mock); - return container.addAnswer(answer, false); + return container.addAnswer(answer, false, null); } protected GenericMetadataSupport actualParameterizedType(Object mock) { diff --git a/src/main/java/org/mockito/stubbing/BaseStubber.java b/src/main/java/org/mockito/stubbing/BaseStubber.java new file mode 100644 index 0000000000..5e4074cef9 --- /dev/null +++ b/src/main/java/org/mockito/stubbing/BaseStubber.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.stubbing; + +import org.mockito.Mockito; + +public interface BaseStubber { + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Throwable[])} style: + *

+     *   doThrow(new RuntimeException("one")).
+     *   doThrow(new RuntimeException("two"))
+     *       .when(mock).someVoidMethod();
+     * 
+ * See javadoc for {@link Mockito#doThrow(Throwable[])} + * + * @param toBeThrown to be thrown when the stubbed method is called + * @return stubber - to select a method for stubbing + */ + Stubber doThrow(Throwable... toBeThrown); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Class)} style: + *

+     *   doThrow(RuntimeException.class).
+     *   doThrow(IllegalArgumentException.class)
+     *       .when(mock).someVoidMethod();
+     * 
+ * See javadoc for {@link Mockito#doThrow(Class)} + * + * @param toBeThrown exception class to be thrown when the stubbed method is called + * @return stubber - to select a method for stubbing + * + * @since 2.1.0 + */ + Stubber doThrow(Class toBeThrown); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Class)} style: + *

+     *   doThrow(RuntimeException.class).
+     *   doThrow(IllegalArgumentException.class)
+     *       .when(mock).someVoidMethod();
+     * 
+ * See javadoc for {@link Mockito#doThrow(Class)} + * + * @param toBeThrown exception class to be thrown when the stubbed method is called + * @param nextToBeThrown exception class next to be thrown when the stubbed method is called + * @return stubber - to select a method for stubbing + * + * @since 2.1.0 + */ + // Additional method helps users of JDK7+ to hide heap pollution / unchecked generics array creation + @SuppressWarnings ({"unchecked", "varargs"}) + Stubber doThrow(Class toBeThrown, Class... nextToBeThrown); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doAnswer(Answer)} style: + *

+     *   doAnswer(answerOne).
+     *   doAnswer(answerTwo)
+     *       .when(mock).someVoidMethod();
+     * 
+ * See javadoc for {@link Mockito#doAnswer(Answer)} + * + * @param answer to answer when the stubbed method is called + * @return stubber - to select a method for stubbing + */ + Stubber doAnswer(Answer answer); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doNothing()} style: + *

+     *   doNothing().
+     *   doThrow(new RuntimeException("two"))
+     *       .when(mock).someVoidMethod();
+     * 
+ * See javadoc for {@link Mockito#doNothing()} + * + * @return stubber - to select a method for stubbing + */ + Stubber doNothing(); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doReturn(Object)} style. + *

+ * See javadoc for {@link Mockito#doReturn(Object)} + * + * @param toBeReturned to be returned when the stubbed method is called + * @return stubber - to select a method for stubbing + */ + Stubber doReturn(Object toBeReturned); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doReturn(Object)} style. + *

+ * See javadoc for {@link Mockito#doReturn(Object, Object...)} + * + * @param toBeReturned to be returned when the stubbed method is called + * @param nextToBeReturned to be returned in consecutive calls when the stubbed method is called + * @return stubber - to select a method for stubbing + */ + @SuppressWarnings({"unchecked", "varargs"}) + Stubber doReturn(Object toBeReturned, Object... nextToBeReturned); + + /** + * Use it for stubbing consecutive calls in {@link Mockito#doCallRealMethod()} style. + *

+ * See javadoc for {@link Mockito#doCallRealMethod()} + * + * @return stubber - to select a method for stubbing + */ + Stubber doCallRealMethod(); +} diff --git a/src/main/java/org/mockito/stubbing/LenientStubber.java b/src/main/java/org/mockito/stubbing/LenientStubber.java new file mode 100644 index 0000000000..59fde18f6e --- /dev/null +++ b/src/main/java/org/mockito/stubbing/LenientStubber.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.stubbing; + +public interface LenientStubber extends BaseStubber { + + OngoingStubbing when(T methodCall); +} diff --git a/src/main/java/org/mockito/stubbing/Stubber.java b/src/main/java/org/mockito/stubbing/Stubber.java index b7507a71d6..6289ddcdbd 100644 --- a/src/main/java/org/mockito/stubbing/Stubber.java +++ b/src/main/java/org/mockito/stubbing/Stubber.java @@ -39,7 +39,7 @@ * See examples in javadoc for {@link Mockito} */ @SuppressWarnings("unchecked") -public interface Stubber { +public interface Stubber extends BaseStubber { /** * Allows to choose a method when stubbing in doThrow()|doAnswer()|doNothing()|doReturn() style @@ -70,111 +70,4 @@ public interface Stubber { * @return select method for stubbing */ T when(T mock); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Throwable[])} style: - *


-     *   doThrow(new RuntimeException("one")).
-     *   doThrow(new RuntimeException("two"))
-     *       .when(mock).someVoidMethod();
-     * 
- * See javadoc for {@link Mockito#doThrow(Throwable[])} - * - * @param toBeThrown to be thrown when the stubbed method is called - * @return stubber - to select a method for stubbing - */ - Stubber doThrow(Throwable... toBeThrown); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Class)} style: - *

-     *   doThrow(RuntimeException.class).
-     *   doThrow(IllegalArgumentException.class)
-     *       .when(mock).someVoidMethod();
-     * 
- * See javadoc for {@link Mockito#doThrow(Class)} - * - * @param toBeThrown exception class to be thrown when the stubbed method is called - * @return stubber - to select a method for stubbing - * - * @since 2.1.0 - */ - Stubber doThrow(Class toBeThrown); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doThrow(Class)} style: - *

-     *   doThrow(RuntimeException.class).
-     *   doThrow(IllegalArgumentException.class)
-     *       .when(mock).someVoidMethod();
-     * 
- * See javadoc for {@link Mockito#doThrow(Class)} - * - * @param toBeThrown exception class to be thrown when the stubbed method is called - * @param nextToBeThrown exception class next to be thrown when the stubbed method is called - * @return stubber - to select a method for stubbing - * - * @since 2.1.0 - */ - // Additional method helps users of JDK7+ to hide heap pollution / unchecked generics array creation - @SuppressWarnings ({"unchecked", "varargs"}) - Stubber doThrow(Class toBeThrown, Class... nextToBeThrown); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doAnswer(Answer)} style: - *

-     *   doAnswer(answerOne).
-     *   doAnswer(answerTwo)
-     *       .when(mock).someVoidMethod();
-     * 
- * See javadoc for {@link Mockito#doAnswer(Answer)} - * - * @param answer to answer when the stubbed method is called - * @return stubber - to select a method for stubbing - */ - Stubber doAnswer(Answer answer); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doNothing()} style: - *

-     *   doNothing().
-     *   doThrow(new RuntimeException("two"))
-     *       .when(mock).someVoidMethod();
-     * 
- * See javadoc for {@link Mockito#doNothing()} - * - * @return stubber - to select a method for stubbing - */ - Stubber doNothing(); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doReturn(Object)} style. - *

- * See javadoc for {@link Mockito#doReturn(Object)} - * - * @param toBeReturned to be returned when the stubbed method is called - * @return stubber - to select a method for stubbing - */ - Stubber doReturn(Object toBeReturned); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doReturn(Object)} style. - *

- * See javadoc for {@link Mockito#doReturn(Object, Object...)} - * - * @param toBeReturned to be returned when the stubbed method is called - * @param nextToBeReturned to be returned in consecutive calls when the stubbed method is called - * @return stubber - to select a method for stubbing - */ - @SuppressWarnings({"unchecked", "varargs"}) - Stubber doReturn(Object toBeReturned, Object... nextToBeReturned); - - /** - * Use it for stubbing consecutive calls in {@link Mockito#doCallRealMethod()} style. - *

- * See javadoc for {@link Mockito#doCallRealMethod()} - * - * @return stubber - to select a method for stubbing - */ - Stubber doCallRealMethod(); } diff --git a/src/main/java/org/mockito/stubbing/Stubbing.java b/src/main/java/org/mockito/stubbing/Stubbing.java index 09856fbbe2..707ef66cff 100644 --- a/src/main/java/org/mockito/stubbing/Stubbing.java +++ b/src/main/java/org/mockito/stubbing/Stubbing.java @@ -7,6 +7,7 @@ import org.mockito.MockingDetails; import org.mockito.NotExtensible; import org.mockito.invocation.Invocation; +import org.mockito.quality.Strictness; /** * Stubbing declared on the mock object. @@ -50,4 +51,6 @@ public interface Stubbing extends Answer { * @since 2.2.3 */ boolean wasUsed(); + + Strictness getStrictness(); } diff --git a/src/test/java/org/mockito/internal/junit/UnusedStubbingsTest.java b/src/test/java/org/mockito/internal/junit/UnusedStubbingsTest.java index 8d11dc2b75..af1d3a186b 100644 --- a/src/test/java/org/mockito/internal/junit/UnusedStubbingsTest.java +++ b/src/test/java/org/mockito/internal/junit/UnusedStubbingsTest.java @@ -4,9 +4,6 @@ */ package org.mockito.internal.junit; -import java.util.Arrays; -import java.util.Collections; - import org.junit.Test; import org.mockito.internal.invocation.InvocationBuilder; import org.mockito.internal.stubbing.StubbedInvocationMatcher; @@ -14,6 +11,9 @@ import org.mockito.stubbing.Stubbing; import org.mockitoutil.TestBase; +import java.util.Arrays; +import java.util.Collections; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.internal.stubbing.answers.DoesNothing.doesNothing; @@ -38,8 +38,8 @@ public void no_unused_stubbings() throws Exception { public void unused_stubbings() throws Exception { //given UnusedStubbings stubbings = new UnusedStubbings(Arrays.asList( - new StubbedInvocationMatcher(new InvocationBuilder().toInvocationMatcher(), doesNothing()), - new StubbedInvocationMatcher(new InvocationBuilder().toInvocationMatcher(), doesNothing()) + new StubbedInvocationMatcher(doesNothing(), new InvocationBuilder().toInvocationMatcher(), null), + new StubbedInvocationMatcher(doesNothing(), new InvocationBuilder().toInvocationMatcher(), null) )); diff --git a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java index de5eb13454..52e481d1c8 100644 --- a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java +++ b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplStubbingTest.java @@ -46,7 +46,7 @@ public void setup() { public void should_finish_stubbing_when_wrong_throwable_is_set() throws Exception { state.stubbingStarted(); try { - invocationContainerImpl.addAnswer(new ThrowsException(new Exception())); + invocationContainerImpl.addAnswer(new ThrowsException(new Exception()), null); fail(); } catch (MockitoException e) { state.validateState(); @@ -56,18 +56,18 @@ public void should_finish_stubbing_when_wrong_throwable_is_set() throws Exceptio @Test public void should_finish_stubbing_on_adding_return_value() throws Exception { state.stubbingStarted(); - invocationContainerImpl.addAnswer(new Returns("test")); + invocationContainerImpl.addAnswer(new Returns("test"), null); state.validateState(); } @Test public void should_get_results_for_methods() throws Throwable { invocationContainerImpl.setInvocationForPotentialStubbing(new InvocationMatcher(simpleMethod)); - invocationContainerImpl.addAnswer(new Returns("simpleMethod")); + invocationContainerImpl.addAnswer(new Returns("simpleMethod"), null); Invocation differentMethod = new InvocationBuilder().differentMethod().toInvocation(); invocationContainerImpl.setInvocationForPotentialStubbing(new InvocationMatcher(differentMethod)); - invocationContainerImpl.addAnswer(new ThrowsException(new MyException())); + invocationContainerImpl.addAnswer(new ThrowsException(new MyException()), null); assertEquals("simpleMethod", invocationContainerImpl.answerTo(simpleMethod)); @@ -80,11 +80,11 @@ public void should_get_results_for_methods() throws Throwable { @Test public void should_get_results_for_methods_stub_only() throws Throwable { invocationContainerImplStubOnly.setInvocationForPotentialStubbing(new InvocationMatcher(simpleMethod)); - invocationContainerImplStubOnly.addAnswer(new Returns("simpleMethod")); + invocationContainerImplStubOnly.addAnswer(new Returns("simpleMethod"), null); Invocation differentMethod = new InvocationBuilder().differentMethod().toInvocation(); invocationContainerImplStubOnly.setInvocationForPotentialStubbing(new InvocationMatcher(differentMethod)); - invocationContainerImplStubOnly.addAnswer(new ThrowsException(new MyException())); + invocationContainerImplStubOnly.addAnswer(new ThrowsException(new MyException()), null); assertEquals("simpleMethod", invocationContainerImplStubOnly.answerTo(simpleMethod)); @@ -97,7 +97,7 @@ public void should_get_results_for_methods_stub_only() throws Throwable { @Test public void should_validate_throwable() throws Throwable { try { - invocationContainerImpl.addAnswer(new ThrowsException(null)); + invocationContainerImpl.addAnswer(new ThrowsException(null), null); fail(); } catch (MockitoException e) {} } diff --git a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplTest.java b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplTest.java index 176372f79d..c63d6e96ae 100644 --- a/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplTest.java +++ b/src/test/java/org/mockito/internal/stubbing/InvocationContainerImplTest.java @@ -4,12 +4,6 @@ */ package org.mockito.internal.stubbing; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.LinkedList; -import java.util.concurrent.CountDownLatch; import org.junit.Test; import org.mockito.internal.creation.MockSettingsImpl; import org.mockito.internal.invocation.InvocationBuilder; @@ -19,6 +13,13 @@ import org.mockito.invocation.Invocation; import org.mockito.mock.MockCreationSettings; +import java.util.LinkedList; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + /** * Author: Szczepan Faber */ @@ -54,7 +55,7 @@ public void run() { throw new RuntimeException(e); } c.setInvocationForPotentialStubbing(new InvocationMatcher(invocation)); - c.addAnswer(new Returns("foo")); + c.addAnswer(new Returns("foo"), null); c.findAnswerFor(invocation); } }; @@ -98,7 +99,7 @@ public void should_tell_if_has_invocation_for_potential_stubbing() throws Except container.setInvocationForPotentialStubbing(new InvocationBuilder().toInvocationMatcher()); assertTrue(container.hasInvocationForPotentialStubbing()); - container.addAnswer(new ReturnsEmptyValues()); + container.addAnswer(new ReturnsEmptyValues(), null); assertFalse(container.hasInvocationForPotentialStubbing()); } @@ -107,7 +108,7 @@ public void should_tell_if_has_invocation_for_potential_stubbing_stub_only() thr containerStubOnly.setInvocationForPotentialStubbing(new InvocationBuilder().toInvocationMatcher()); assertTrue(containerStubOnly.hasInvocationForPotentialStubbing()); - containerStubOnly.addAnswer(new ReturnsEmptyValues()); + containerStubOnly.addAnswer(new ReturnsEmptyValues(), null); assertFalse(containerStubOnly.hasInvocationForPotentialStubbing()); } } diff --git a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java index aa4c3e6298..13b0e2ee63 100644 --- a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java @@ -4,6 +4,10 @@ */ package org.mockitousage.stubbing; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowableAssert; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import org.mockito.MockitoSession; @@ -11,18 +15,28 @@ import org.mockito.quality.Strictness; import org.mockitousage.IMethods; -import static org.junit.Assert.fail; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.withSettings; public class StrictnessPerMockTest { - MockitoSession mockito = Mockito.mockitoSession().strictness(Strictness.STRICT_STUBS).startMocking(); + MockitoSession mockito; + + @Before + public void setup() { + mockito = Mockito.mockitoSession().strictness(Strictness.STRICT_STUBS).startMocking(); + } + + @After + public void after() { + mockito.finishMocking(); + } @Test public void strictness_per_mock() throws Throwable { //given - IMethods mock = mock(IMethods.class); + final IMethods mock = mock(IMethods.class); IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); //when @@ -33,12 +47,32 @@ public class StrictnessPerMockTest { lenientMock.simpleMethod(200); //on other mock, we will get strict stubbing exception - try { - mock.simpleMethod(200); - fail(); - } catch (PotentialStubbingProblem e) {} + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(200); + } + }).isInstanceOf(PotentialStubbingProblem.class); + } - //we are not reporting unused stubbings here: - mockito.finishMocking(); + @Test public void strictness_per_stubbing() throws Throwable { + //given + final IMethods mock = mock(IMethods.class); + + //when + given(mock.simpleMethod("1")).willReturn("1"); + + lenient().when(mock.differentMethod("1")).thenReturn("1"); + + //on lenient mock, we can call the stubbed method with different argument: + mock.differentMethod("200"); + + //on other mock, we will get strict stubbing exception + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(200); + } + }).isInstanceOf(PotentialStubbingProblem.class); } } From 876d4584867de847ad1022ad74c22c4120a672c7 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 08:59:25 -0800 Subject: [PATCH 03/50] Reduced duplication in test --- .../mockitousage/stubbing/StrictnessPerMockTest.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java index 13b0e2ee63..0059942cc3 100644 --- a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java @@ -9,6 +9,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.exceptions.misusing.PotentialStubbingProblem; @@ -23,10 +24,12 @@ public class StrictnessPerMockTest { MockitoSession mockito; + @Mock IMethods mock; + IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); @Before public void setup() { - mockito = Mockito.mockitoSession().strictness(Strictness.STRICT_STUBS).startMocking(); + mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); } @After @@ -35,10 +38,6 @@ public void after() { } @Test public void strictness_per_mock() throws Throwable { - //given - final IMethods mock = mock(IMethods.class); - IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); - //when given(lenientMock.simpleMethod(100)).willReturn("100"); given(mock.simpleMethod(100)).willReturn("100"); @@ -56,9 +55,6 @@ public void call() throws Throwable { } @Test public void strictness_per_stubbing() throws Throwable { - //given - final IMethods mock = mock(IMethods.class); - //when given(mock.simpleMethod("1")).willReturn("1"); From f00f140f61be83cab2a12683de688da91240682a Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 09:10:16 -0800 Subject: [PATCH 04/50] Improved the test case --- .../stubbing/StrictnessPerMockTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java index 0059942cc3..b0a86db07d 100644 --- a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java @@ -13,12 +13,14 @@ import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.verification.NoInteractionsWanted; import org.mockito.quality.Strictness; import org.mockitousage.IMethods; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.withSettings; public class StrictnessPerMockTest { @@ -44,8 +46,17 @@ public void after() { //on lenient mock, we can call the stubbed method with different argument: lenientMock.simpleMethod(200); + //and stubbing will be reported in 'verifyNoMoreInteractions' + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + verifyNoMoreInteractions(lenientMock); + } + }).isInstanceOf(NoInteractionsWanted.class); - //on other mock, we will get strict stubbing exception + //on the mock with strict stubs, the stubbing is implicitly verified so 'verifyNoMoreInteractions' succeeds: + verifyNoMoreInteractions(mock); + //and we will get strict stubbing exception if there is declared stubbing with different argument: Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { @Override public void call() throws Throwable { From 218296a596e299cb6a9b004b55dcec0ca1567550 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 19:54:15 -0800 Subject: [PATCH 05/50] Added handy test utility --- src/test/java/org/mockitoutil/TestBase.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/org/mockitoutil/TestBase.java b/src/test/java/org/mockitoutil/TestBase.java index ca3d7b8744..98066ebc33 100644 --- a/src/test/java/org/mockitoutil/TestBase.java +++ b/src/test/java/org/mockitoutil/TestBase.java @@ -5,6 +5,7 @@ package org.mockitoutil; +import org.assertj.core.api.Condition; import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; @@ -31,6 +32,18 @@ */ public class TestBase { + /** + * Condition to be used with AssertJ + */ + public static Condition hasMessageContaining(final String substring) { + return new Condition() { + @Override + public boolean matches(Throwable e) { + return e.getMessage().contains(substring); + } + }; + } + @After public void cleanUpConfigInAnyCase() { ConfigurationAccess.getConfig().overrideCleansStackTrace(false); From 480a9ec903ff69b37c52d9f2f0c38d0608792fae Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 19:54:36 -0800 Subject: [PATCH 06/50] Reworked the tests for clarity --- .../strictness/StrictnessPerMockTest.java | 100 ++++++++++++++++++ .../strictness/StrictnessPerStubbingTest.java | 98 +++++++++++++++++ .../stubbing/StrictnessPerMockTest.java | 85 --------------- 3 files changed, 198 insertions(+), 85 deletions(-) create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java delete mode 100644 src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java new file mode 100644 index 0000000000..ec410ac2e7 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -0,0 +1,100 @@ +package org.mockitousage.strictness; + +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowableAssert; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.misusing.UnnecessaryStubbingException; +import org.mockito.exceptions.verification.NoInteractionsWanted; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; +import org.mockitoutil.TestBase; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.withSettings; + +//TODO also move other Strictness tests to this package (unless they already have good package) +public class StrictnessPerMockTest { + + MockitoSession mockito; + @Mock IMethods strictStubsMock; + IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); + + @Before + public void before() { + mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); + } + + @Test + public void potential_stubbing_problem() { + //when + given(lenientMock.simpleMethod(100)).willReturn("100"); + given(strictStubsMock.simpleMethod(100)).willReturn("100"); + + //then on lenient mock (created by hand), we can call the stubbed method with different arg: + lenientMock.simpleMethod(200); + + //and on strict stub mock (created by session), we cannot call stubbed method with different arg: + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + strictStubsMock.simpleMethod(200); + } + }).isInstanceOf(PotentialStubbingProblem.class); + } + + @Test + public void unnecessary_stubbing() { + //when + given(lenientMock.simpleMethod(100)).willReturn("100"); + given(strictStubsMock.simpleMethod(100)).willReturn("100"); + + //then unnecessary stubbing flags method only on the strict stub mock: + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mockito.finishMocking(); + } + }).isInstanceOf(UnnecessaryStubbingException.class) + .hasMessageContaining("1. -> ") + //good enough to prove that we're flagging just one unnecessary stubbing: + //TODO: let's make UnnecessaryStubbingException exception contain the Invocation instance + //so that we can write clean assertion rather than depending on string + .isNot(TestBase.hasMessageContaining("2. ->")); + } + + @Test + public void verify_no_more_invocations() { + //when + given(lenientMock.simpleMethod(100)).willReturn("100"); + given(strictStubsMock.simpleMethod(100)).willReturn("100"); + + //and: + strictStubsMock.simpleMethod(100); + lenientMock.simpleMethod(100); + + //then 'verifyNoMoreInteractions' ignores strict stub (implicitly verified) but flags the lenient mock + Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + verifyNoMoreInteractions(strictStubsMock, lenientMock); + } + }).isInstanceOf(NoInteractionsWanted.class) + .hasMessageContaining("But found this interaction on mock 'lenientMock'") + //TODO: let's make NoInteractionsWanted exception contain the Invocation instances + //so that we can write clean assertion rather than depending on string + .hasMessageContaining("Actually, above is the only interaction with this mock"); + } + + @After + public void after() { + mockito.finishMocking(); + } +} diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java new file mode 100644 index 0000000000..8f89239964 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -0,0 +1,98 @@ +package org.mockitousage.strictness; + +import org.assertj.core.api.ThrowableAssert; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.misusing.UnnecessaryStubbingException; +import org.mockito.exceptions.verification.NoInteractionsWanted; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; +import org.mockitoutil.TestBase; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class StrictnessPerStubbingTest { + + MockitoSession mockito; + @Mock IMethods mock; + + @Before + public void before() { + mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); + } + + @Test + public void potential_stubbing_problem() { + //when + when(mock.simpleMethod("1")).thenReturn("1"); + lenient().when(mock.differentMethod("2")).thenReturn("2"); + + //then on lenient stubbing, we can call it with different argument: + mock.differentMethod("200"); + + //but on strict stubbing, we cannot: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod("100"); + } + }).isInstanceOf(PotentialStubbingProblem.class); + } + + @Test + @Ignore("TODO") + public void unnecessary_stubbing() { + //when + when(mock.simpleMethod("1")).thenReturn("1"); + lenient().when(mock.differentMethod("2")).thenReturn("2"); + + //then unnecessary stubbing flags method only on the strict stubbing: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mockito.finishMocking(); + } + }).isInstanceOf(UnnecessaryStubbingException.class) + .hasMessageContaining("1. -> ") + //good enough to prove that we're flagging just one unnecessary stubbing: + //TODO: this assertion is duplicated with StrictnessPerMockTest + .isNot(TestBase.hasMessageContaining("2. ->")); + } + + @Test + @Ignore("TODO") + public void verify_no_more_invocations() { + //when + when(mock.simpleMethod("1")).thenReturn("1"); + lenient().when(mock.differentMethod("2")).thenReturn("2"); + + //and: + mock.simpleMethod("1"); + mock.differentMethod("200"); // <- different arg + + //then 'verifyNoMoreInteractions' ignores strict stub (implicitly verified) but flags the lenient stubbing (called with different arg) + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + verifyNoMoreInteractions(mock); + } + }).isInstanceOf(NoInteractionsWanted.class) + .hasMessageContaining("But found this interaction on mock") + //TODO: assertion duplicated with StrictnessPerMockTest + .hasMessageContaining("Actually, above is the only interaction with this mock"); + } + + @After + public void after() { + mockito.finishMocking(); + } +} diff --git a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java deleted file mode 100644 index b0a86db07d..0000000000 --- a/src/test/java/org/mockitousage/stubbing/StrictnessPerMockTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2017 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockitousage.stubbing; - -import org.assertj.core.api.Assertions; -import org.assertj.core.api.ThrowableAssert; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoSession; -import org.mockito.exceptions.misusing.PotentialStubbingProblem; -import org.mockito.exceptions.verification.NoInteractionsWanted; -import org.mockito.quality.Strictness; -import org.mockitousage.IMethods; - -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.withSettings; - -public class StrictnessPerMockTest { - - MockitoSession mockito; - @Mock IMethods mock; - IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); - - @Before - public void setup() { - mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); - } - - @After - public void after() { - mockito.finishMocking(); - } - - @Test public void strictness_per_mock() throws Throwable { - //when - given(lenientMock.simpleMethod(100)).willReturn("100"); - given(mock.simpleMethod(100)).willReturn("100"); - - //on lenient mock, we can call the stubbed method with different argument: - lenientMock.simpleMethod(200); - //and stubbing will be reported in 'verifyNoMoreInteractions' - Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override - public void call() throws Throwable { - verifyNoMoreInteractions(lenientMock); - } - }).isInstanceOf(NoInteractionsWanted.class); - - //on the mock with strict stubs, the stubbing is implicitly verified so 'verifyNoMoreInteractions' succeeds: - verifyNoMoreInteractions(mock); - //and we will get strict stubbing exception if there is declared stubbing with different argument: - Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override - public void call() throws Throwable { - mock.simpleMethod(200); - } - }).isInstanceOf(PotentialStubbingProblem.class); - } - - @Test public void strictness_per_stubbing() throws Throwable { - //when - given(mock.simpleMethod("1")).willReturn("1"); - - lenient().when(mock.differentMethod("1")).thenReturn("1"); - - //on lenient mock, we can call the stubbed method with different argument: - mock.differentMethod("200"); - - //on other mock, we will get strict stubbing exception - Assertions.assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override - public void call() throws Throwable { - mock.simpleMethod(200); - } - }).isInstanceOf(PotentialStubbingProblem.class); - } -} From 7e13b4de3423c868fec6ab0c0983df61f34a8346 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 20:09:47 -0800 Subject: [PATCH 07/50] Support for strictness per stubbing --- .../internal/junit/UnusedStubbingsFinder.java | 16 ++++++++++++---- .../junit/UnusedStubbingsFinderTest.java | 18 ++++++++++++++++++ .../strictness/StrictnessPerStubbingTest.java | 1 - 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 715c39326a..2fbc0a7030 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -4,12 +4,18 @@ */ package org.mockito.internal.junit; -import org.mockito.stubbing.Stubbing; import org.mockito.internal.invocation.finder.AllInvocationsFinder; import org.mockito.internal.util.collections.ListUtil.Filter; import org.mockito.invocation.Invocation; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Stubbing; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import static org.mockito.internal.util.collections.ListUtil.filter; @@ -19,14 +25,16 @@ public class UnusedStubbingsFinder { /** - * Gets all unused stubbings for given set of mock objects, in order + * Gets all unused stubbings for given set of mock objects, in order. + * Stubbings explicitily marked as LENIENT are not included. */ public UnusedStubbings getUnusedStubbings(Iterable mocks) { Set stubbings = AllInvocationsFinder.findStubbings(mocks); List unused = filter(stubbings, new Filter() { public boolean isOut(Stubbing s) { - return s.wasUsed(); + //TODO 792, Strictness.LENIENT -> we want to do the same thing for getUnusedStubbingsByLocation() + return s.wasUsed() || s.getStrictness() == Strictness.LENIENT; } }); diff --git a/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java b/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java index b03d5d7959..63f23d0aa0 100644 --- a/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java +++ b/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java @@ -16,6 +16,7 @@ import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; /** @@ -85,6 +86,23 @@ public void some_unused_stubbings() throws Exception { stubbings.toString()); } + @Test + public void unused_and_lenient_stubbings() throws Exception { + when(mock1.simpleMethod(1)).thenReturn("1"); + when(mock1.simpleMethod(2)).thenReturn("2"); + lenient().when(mock2.simpleMethod(3)).thenReturn("3"); + + mock1.simpleMethod(1); + + //when + UnusedStubbings stubbings = finder.getUnusedStubbings((List) asList(mock1, mock2)); + + //then + assertEquals(1, stubbings.size()); + assertEquals("[mock1.simpleMethod(2); stubbed with: [Returns: 2]]", + stubbings.toString()); + } + @Test public void some_unused_stubbings_by_location() throws Exception { when(mock1.simpleMethod(1)).thenReturn("1"); diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 8f89239964..eb004d79ac 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -49,7 +49,6 @@ public void call() throws Throwable { } @Test - @Ignore("TODO") public void unnecessary_stubbing() { //when when(mock.simpleMethod("1")).thenReturn("1"); From a10d22dbf2ab44d52ed85012706c59c11b31166d Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 4 Jan 2018 20:10:54 -0800 Subject: [PATCH 08/50] Updated TODOs --- .../org/mockitousage/strictness/StrictnessPerMockTest.java | 6 +++--- .../mockitousage/strictness/StrictnessPerStubbingTest.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java index ec410ac2e7..56326f82c0 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -20,7 +20,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.withSettings; -//TODO also move other Strictness tests to this package (unless they already have good package) +//TODO 792 also move other Strictness tests to this package (unless they already have good package) public class StrictnessPerMockTest { MockitoSession mockito; @@ -65,7 +65,7 @@ public void call() throws Throwable { }).isInstanceOf(UnnecessaryStubbingException.class) .hasMessageContaining("1. -> ") //good enough to prove that we're flagging just one unnecessary stubbing: - //TODO: let's make UnnecessaryStubbingException exception contain the Invocation instance + //TODO 792: let's make UnnecessaryStubbingException exception contain the Invocation instance //so that we can write clean assertion rather than depending on string .isNot(TestBase.hasMessageContaining("2. ->")); } @@ -88,7 +88,7 @@ public void call() throws Throwable { } }).isInstanceOf(NoInteractionsWanted.class) .hasMessageContaining("But found this interaction on mock 'lenientMock'") - //TODO: let's make NoInteractionsWanted exception contain the Invocation instances + //TODO 792: let's make NoInteractionsWanted exception contain the Invocation instances //so that we can write clean assertion rather than depending on string .hasMessageContaining("Actually, above is the only interaction with this mock"); } diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index eb004d79ac..c9436c0190 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -63,7 +63,7 @@ public void call() throws Throwable { }).isInstanceOf(UnnecessaryStubbingException.class) .hasMessageContaining("1. -> ") //good enough to prove that we're flagging just one unnecessary stubbing: - //TODO: this assertion is duplicated with StrictnessPerMockTest + //TODO 792: this assertion is duplicated with StrictnessPerMockTest .isNot(TestBase.hasMessageContaining("2. ->")); } @@ -86,7 +86,7 @@ public void call() throws Throwable { } }).isInstanceOf(NoInteractionsWanted.class) .hasMessageContaining("But found this interaction on mock") - //TODO: assertion duplicated with StrictnessPerMockTest + //TODO 792: assertion duplicated with StrictnessPerMockTest .hasMessageContaining("Actually, above is the only interaction with this mock"); } From e5dd9d74eb09efe0e5ab2ef36399aaea3bfe331e Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 Jan 2018 19:50:58 -0800 Subject: [PATCH 09/50] Updated the test case Removed the ignored flag. --- .../strictness/StrictnessPerStubbingTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index c9436c0190..513a78e4db 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -3,7 +3,6 @@ import org.assertj.core.api.ThrowableAssert; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; @@ -68,7 +67,6 @@ public void call() throws Throwable { } @Test - @Ignore("TODO") public void verify_no_more_invocations() { //when when(mock.simpleMethod("1")).thenReturn("1"); @@ -78,16 +76,18 @@ public void verify_no_more_invocations() { mock.simpleMethod("1"); mock.differentMethod("200"); // <- different arg - //then 'verifyNoMoreInteractions' ignores strict stub (implicitly verified) but flags the lenient stubbing (called with different arg) + //then 'verifyNoMoreInteractions' flags the lenient stubbing (called with different arg) + //and reports it with [?] in the exception message assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { @Override public void call() throws Throwable { verifyNoMoreInteractions(mock); } }).isInstanceOf(NoInteractionsWanted.class) - .hasMessageContaining("But found this interaction on mock") + .hasMessageContaining("1. ->") + .hasMessageContaining("2. [?]->"); //TODO 792: assertion duplicated with StrictnessPerMockTest - .hasMessageContaining("Actually, above is the only interaction with this mock"); + // and we should use assertions based on content of the exception rather than the string } @After From d4566d5dc482316bb478c42d3111e0aa875564a5 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 Jan 2018 20:04:46 -0800 Subject: [PATCH 10/50] Updated TODO --- .../java/org/mockito/internal/stubbing/OngoingStubbingImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java index ad95d90882..1bf2414bcb 100644 --- a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java @@ -24,7 +24,7 @@ public OngoingStubbingImpl(InvocationContainerImpl invocationContainer) { @Override public OngoingStubbing thenAnswer(Answer answer) { - //TODO SF! rename all iOngoingStubbing -> ongoingStubbing + //TODO 792 rename all iOngoingStubbing -> ongoingStubbing if(!invocationContainer.hasInvocationForPotentialStubbing()) { throw incorrectUseOfApi(); } From fc4b6074ea094b072586de649047c082f58feaa9 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 Jan 2018 20:05:11 -0800 Subject: [PATCH 11/50] Updated Javadoc --- src/main/java/org/mockito/stubbing/BaseStubber.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/mockito/stubbing/BaseStubber.java b/src/main/java/org/mockito/stubbing/BaseStubber.java index 5e4074cef9..2861dd204e 100644 --- a/src/main/java/org/mockito/stubbing/BaseStubber.java +++ b/src/main/java/org/mockito/stubbing/BaseStubber.java @@ -5,7 +5,12 @@ package org.mockito.stubbing; import org.mockito.Mockito; +import org.mockito.NotExtensible; +/** + * Base interface for stubbing consecutive method calls with {@link Mockito#doReturn(Object)} syntax. + */ +@NotExtensible public interface BaseStubber { /** From d8855431f9c59ee082ebe3427a0b263a7cdae51d Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 5 Jan 2018 20:13:23 -0800 Subject: [PATCH 12/50] Updated Javadoc --- src/main/java/org/mockito/CheckReturnValue.java | 2 +- src/main/java/org/mockito/stubbing/BaseStubber.java | 1 + .../java/org/mockito/stubbing/LenientStubber.java | 12 ++++++++++++ src/main/java/org/mockito/stubbing/Stubber.java | 2 ++ .../strictness/StrictnessPerStubbingTest.java | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/CheckReturnValue.java b/src/main/java/org/mockito/CheckReturnValue.java index 59bfc333fb..0498c148a8 100644 --- a/src/main/java/org/mockito/CheckReturnValue.java +++ b/src/main/java/org/mockito/CheckReturnValue.java @@ -28,5 +28,5 @@ ElementType.TYPE }) @Retention(RetentionPolicy.CLASS) -@interface CheckReturnValue { +public @interface CheckReturnValue { } diff --git a/src/main/java/org/mockito/stubbing/BaseStubber.java b/src/main/java/org/mockito/stubbing/BaseStubber.java index 2861dd204e..b9c30ef6e4 100644 --- a/src/main/java/org/mockito/stubbing/BaseStubber.java +++ b/src/main/java/org/mockito/stubbing/BaseStubber.java @@ -9,6 +9,7 @@ /** * Base interface for stubbing consecutive method calls with {@link Mockito#doReturn(Object)} syntax. + * This interface is needed so that we can reuse the same hierarchy in subinterfaces. */ @NotExtensible public interface BaseStubber { diff --git a/src/main/java/org/mockito/stubbing/LenientStubber.java b/src/main/java/org/mockito/stubbing/LenientStubber.java index 59fde18f6e..a163156826 100644 --- a/src/main/java/org/mockito/stubbing/LenientStubber.java +++ b/src/main/java/org/mockito/stubbing/LenientStubber.java @@ -4,7 +4,19 @@ */ package org.mockito.stubbing; +import org.mockito.CheckReturnValue; +import org.mockito.Mockito; +import org.mockito.NotExtensible; + +/** + * Used for declaring optional stubbings with {@link Mockito#lenient()} + */ +@NotExtensible public interface LenientStubber extends BaseStubber { + /** + * Allows declaring the method to stub. See {@link Mockito#when(Object)} + */ + @CheckReturnValue OngoingStubbing when(T methodCall); } diff --git a/src/main/java/org/mockito/stubbing/Stubber.java b/src/main/java/org/mockito/stubbing/Stubber.java index 6289ddcdbd..5de954bf78 100644 --- a/src/main/java/org/mockito/stubbing/Stubber.java +++ b/src/main/java/org/mockito/stubbing/Stubber.java @@ -5,6 +5,7 @@ package org.mockito.stubbing; import org.mockito.Mockito; +import org.mockito.NotExtensible; /** * Allows to choose a method when stubbing in doThrow()|doAnswer()|doNothing()|doReturn() style @@ -39,6 +40,7 @@ * See examples in javadoc for {@link Mockito} */ @SuppressWarnings("unchecked") +@NotExtensible public interface Stubber extends BaseStubber { /** diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 513a78e4db..f000e59ac5 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -33,6 +33,7 @@ public void before() { public void potential_stubbing_problem() { //when when(mock.simpleMethod("1")).thenReturn("1"); + //TODO 792: consider renaming lenient() -> optional() lenient().when(mock.differentMethod("2")).thenReturn("2"); //then on lenient stubbing, we can call it with different argument: From db4912e338a991a31429acfa7c64b4cefd3f0240 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sun, 7 Jan 2018 23:34:43 -0800 Subject: [PATCH 13/50] Implemented 'lenient' for 'doAnswer' style stubbing --- .../org/mockito/internal/MockitoCore.java | 11 ++--- .../stubbing/DefaultLenientStubber.java | 2 +- .../stubbing/DoAnswerStyleStubbing.java | 42 +++++++++++++++++++ .../stubbing/InvocationContainerImpl.java | 18 ++++---- .../internal/stubbing/StubberImpl.java | 9 +++- .../org/mockito/stubbing/LenientStubber.java | 3 +- .../strictness/StrictnessPerStubbingTest.java | 17 ++++++++ 7 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java diff --git a/src/main/java/org/mockito/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java index 28809b55e9..c764e59825 100644 --- a/src/main/java/org/mockito/internal/MockitoCore.java +++ b/src/main/java/org/mockito/internal/MockitoCore.java @@ -26,6 +26,7 @@ import org.mockito.invocation.Invocation; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; +import org.mockito.quality.Strictness; import org.mockito.stubbing.LenientStubber; import org.mockito.stubbing.OngoingStubbing; import org.mockito.stubbing.Stubber; @@ -167,18 +168,14 @@ public InOrder inOrder(Object... mocks) { } public Stubber stubber() { - return createStubber(false); + return stubber(null); } - public Stubber lenientStubber() { - return createStubber(true); - } - - private Stubber createStubber(boolean lenient) { + public Stubber stubber(Strictness strictness) { MockingProgress mockingProgress = mockingProgress(); mockingProgress.stubbingStarted(); mockingProgress.resetOngoingStubbing(); - return new StubberImpl(); + return new StubberImpl(strictness); } public void validateMockitoUsage() { diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index 65c107d804..757cebee14 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -42,7 +42,7 @@ public Stubber doNothing() { @Override public Stubber doReturn(Object toBeReturned) { - return null; + return MOCKITO_CORE.stubber(Strictness.LENIENT).doReturn(toBeReturned); } @Override diff --git a/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java new file mode 100644 index 0000000000..5b249563de --- /dev/null +++ b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.stubbing; + +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holds answers declared using 'doAnswer' stubbing style. + */ +class DoAnswerStyleStubbing { + + private final List> answers = new ArrayList>(); + private Strictness strictness; + + void setAnswers(List> answers, Strictness strictness) { + this.strictness = strictness; + this.answers.addAll(answers); + } + + boolean isEmpty() { + return answers.isEmpty(); + } + + void clear() { + answers.clear(); + strictness = null; + } + + List> getAnswers() { + return answers; + } + + Strictness getStrictness() { + return strictness; + } +} diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 4c66e74f8f..5aef462cff 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -18,7 +18,6 @@ import org.mockito.stubbing.ValidableAnswer; import java.io.Serializable; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -29,7 +28,7 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl private static final long serialVersionUID = -5334301962749537177L; private final LinkedList stubbed = new LinkedList(); - private final List> answersForStubbing = new ArrayList>(); + private final DoAnswerStyleStubbing doAnswerStyleStubbing = new DoAnswerStyleStubbing(); private final RegisteredInvocations registeredInvocations; private MatchableInvocation invocationForStubbing; @@ -95,12 +94,15 @@ public StubbedInvocationMatcher findAnswerFor(Invocation invocation) { return null; } - public void setAnswersForStubbing(List> answers) { - answersForStubbing.addAll(answers); + /** + * Sets the answers declared with 'doAnswer' style. + */ + public void setAnswersForStubbing(List> answers, Strictness strictness) { + doAnswerStyleStubbing.setAnswers(answers, strictness); } public boolean hasAnswersForStubbing() { - return !answersForStubbing.isEmpty(); + return !doAnswerStyleStubbing.isEmpty(); } public boolean hasInvocationForPotentialStubbing() { @@ -110,10 +112,10 @@ public boolean hasInvocationForPotentialStubbing() { public void setMethodForStubbing(MatchableInvocation invocation) { invocationForStubbing = invocation; assert hasAnswersForStubbing(); - for (int i = 0; i < answersForStubbing.size(); i++) { - addAnswer(answersForStubbing.get(i), i != 0, null); + for (int i = 0; i < doAnswerStyleStubbing.getAnswers().size(); i++) { + addAnswer(doAnswerStyleStubbing.getAnswers().get(i), i != 0, doAnswerStyleStubbing.getStrictness()); } - answersForStubbing.clear(); + doAnswerStyleStubbing.clear(); } @Override diff --git a/src/main/java/org/mockito/internal/stubbing/StubberImpl.java b/src/main/java/org/mockito/internal/stubbing/StubberImpl.java index 7771c0438f..2172809e36 100644 --- a/src/main/java/org/mockito/internal/stubbing/StubberImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/StubberImpl.java @@ -8,6 +8,7 @@ import org.mockito.internal.stubbing.answers.Returns; import org.mockito.internal.stubbing.answers.ThrowsException; import org.mockito.internal.util.MockUtil; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import org.mockito.stubbing.Stubber; @@ -24,6 +25,12 @@ public class StubberImpl implements Stubber { + private final Strictness strictness; + + public StubberImpl(Strictness strictness) { + this.strictness = strictness; + } + private final List> answers = new LinkedList>(); @Override @@ -36,7 +43,7 @@ public T when(T mock) { throw notAMockPassedToWhenMethod(); } - MockUtil.getInvocationContainer(mock).setAnswersForStubbing(answers); + MockUtil.getInvocationContainer(mock).setAnswersForStubbing(answers, strictness); return mock; } diff --git a/src/main/java/org/mockito/stubbing/LenientStubber.java b/src/main/java/org/mockito/stubbing/LenientStubber.java index a163156826..4dc340c932 100644 --- a/src/main/java/org/mockito/stubbing/LenientStubber.java +++ b/src/main/java/org/mockito/stubbing/LenientStubber.java @@ -15,7 +15,8 @@ public interface LenientStubber extends BaseStubber { /** - * Allows declaring the method to stub. See {@link Mockito#when(Object)} + * Allows declaring the method to stub. See {@link Mockito#when(Object)}. + * Needed for classic stubbing with when().then() */ @CheckReturnValue OngoingStubbing when(T methodCall); diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index f000e59ac5..fd48c75981 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -15,6 +15,7 @@ import org.mockitoutil.TestBase; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -48,6 +49,22 @@ public void call() throws Throwable { }).isInstanceOf(PotentialStubbingProblem.class); } + @Test + public void doReturn_syntax() { + //when + lenient() + .doReturn("2") + .doReturn("3") + .when(mock).simpleMethod(1); + + //then on lenient stubbing, we can call it with different argument: + mock.differentMethod("200"); + + //and stubbing works, too: + assertEquals("2", mock.simpleMethod(1)); + assertEquals("3", mock.simpleMethod(1)); + } + @Test public void unnecessary_stubbing() { //when From 4eeed70a402bab6e07fccf97004400204caea1ac Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 8 Jan 2018 16:20:17 -0800 Subject: [PATCH 14/50] Implemented 'lenient' for 'doAnswer' varargs style stubbing --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index 757cebee14..1fee3042be 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -47,7 +47,7 @@ public Stubber doReturn(Object toBeReturned) { @Override public Stubber doReturn(Object toBeReturned, Object... nextToBeReturned) { - return null; + return MOCKITO_CORE.stubber(Strictness.LENIENT).doReturn(toBeReturned, nextToBeReturned); } @Override diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index fd48c75981..1c01357c8a 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -52,9 +52,21 @@ public void call() throws Throwable { @Test public void doReturn_syntax() { //when - lenient() - .doReturn("2") - .doReturn("3") + lenient().doReturn("2").doReturn("3") + .when(mock).simpleMethod(1); + + //then on lenient stubbing, we can call it with different argument: + mock.differentMethod("200"); + + //and stubbing works, too: + assertEquals("2", mock.simpleMethod(1)); + assertEquals("3", mock.simpleMethod(1)); + } + + @Test + public void doReturn_varargs_syntax() { + //when + lenient().doReturn("2", "3") .when(mock).simpleMethod(1); //then on lenient stubbing, we can call it with different argument: From 8102ebf4e2e7b5e6a86fc52b6a459e20163b1331 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 8 Jan 2018 16:55:13 -0800 Subject: [PATCH 15/50] Implemented lenient 'doCallRealMethod' stubbing --- .../stubbing/DefaultLenientStubber.java | 10 ++++++--- .../strictness/StrictnessPerStubbingTest.java | 22 ++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index 1fee3042be..8d43bdde60 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -42,17 +42,17 @@ public Stubber doNothing() { @Override public Stubber doReturn(Object toBeReturned) { - return MOCKITO_CORE.stubber(Strictness.LENIENT).doReturn(toBeReturned); + return stubber().doReturn(toBeReturned); } @Override public Stubber doReturn(Object toBeReturned, Object... nextToBeReturned) { - return MOCKITO_CORE.stubber(Strictness.LENIENT).doReturn(toBeReturned, nextToBeReturned); + return stubber().doReturn(toBeReturned, nextToBeReturned); } @Override public Stubber doCallRealMethod() { - return null; + return stubber().doCallRealMethod(); } @Override @@ -61,4 +61,8 @@ public OngoingStubbing when(T methodCall) { ongoingStubbing.setStrictness(Strictness.LENIENT); return ongoingStubbing; } + + private static Stubber stubber() { + return MOCKITO_CORE.stubber(Strictness.LENIENT); + } } diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 1c01357c8a..4c68cbc0d4 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -69,7 +70,7 @@ public void doReturn_varargs_syntax() { lenient().doReturn("2", "3") .when(mock).simpleMethod(1); - //then on lenient stubbing, we can call it with different argument: + //then on lenient stubbing, we can call it with different argument with no exception: mock.differentMethod("200"); //and stubbing works, too: @@ -77,6 +78,25 @@ public void doReturn_varargs_syntax() { assertEquals("3", mock.simpleMethod(1)); } + static class Counter { + int increment(int x) { + return x + 1; + } + } + + @Test + public void doCallRealMethod_syntax() { + //when + Counter mock = mock(Counter.class); + lenient().doCallRealMethod().when(mock).increment(1); + + //then no exception and default return value if we call it with different arg: + assertEquals(0, mock.increment(0)); + + //and real method is called when using correct arg: + assertEquals(2, mock.increment(1)); + } + @Test public void unnecessary_stubbing() { //when From 449757a80302f88d3fa623b87a945129bf680857 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 10 Jan 2018 06:27:27 -0800 Subject: [PATCH 16/50] Implemented lenient 'doNothing' stubbing --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index 8d43bdde60..d0a26d2af8 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -37,7 +37,7 @@ public Stubber doAnswer(Answer answer) { @Override public Stubber doNothing() { - return null; + return stubber().doNothing(); } @Override diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 4c68cbc0d4..c88d770dd8 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -82,6 +83,9 @@ static class Counter { int increment(int x) { return x + 1; } + void scream(String message) { + throw new RuntimeException(message); + } } @Test @@ -97,6 +101,24 @@ public void doCallRealMethod_syntax() { assertEquals(2, mock.increment(1)); } + @Test + public void doNothing_syntax() { + //when + final Counter spy = spy(Counter.class); + lenient().doNothing().when(spy).scream("1"); + + //then no stubbing exception and real method is called if we call stubbed method with different arg: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + spy.scream("2"); + } + }).hasMessage("2"); + + //and we do nothing when stubbing called with correct arg: + spy.scream("1"); + } + @Test public void unnecessary_stubbing() { //when From 70a385d0f2f5c064f7bbbc5b311940fa1867bb9c Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 10 Jan 2018 06:52:58 -0800 Subject: [PATCH 17/50] Implemented lenient 'doAnswer' stubbing Also fixed an existing tests, they were not correct --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index d0a26d2af8..ed0968b91e 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -32,7 +32,7 @@ public Stubber doThrow(Class toBeThrown, Class Date: Wed, 10 Jan 2018 07:00:39 -0800 Subject: [PATCH 18/50] Added license headers --- .../org/mockitousage/strictness/StrictnessPerMockTest.java | 5 +++++ .../mockitousage/strictness/StrictnessPerStubbingTest.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java index 56326f82c0..37c8071dcb 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + package org.mockitousage.strictness; import org.assertj.core.api.Assertions; diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index bce931f8be..800ae71124 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + package org.mockitousage.strictness; import org.assertj.core.api.ThrowableAssert; From 471bf33b2898a3d8ed1843abd49efaf00bfd5938 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 10 Jan 2018 22:14:19 -0800 Subject: [PATCH 19/50] Fixed Serializable problem --- .../org/mockito/internal/stubbing/DoAnswerStyleStubbing.java | 5 +++-- .../mockito/internal/stubbing/InvocationContainerImpl.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java index 5b249563de..d10add4903 100644 --- a/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java +++ b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java @@ -7,13 +7,14 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Holds answers declared using 'doAnswer' stubbing style. */ -class DoAnswerStyleStubbing { +class DoAnswerStyleStubbing implements Serializable { private final List> answers = new ArrayList>(); private Strictness strictness; @@ -23,7 +24,7 @@ void setAnswers(List> answers, Strictness strictness) { this.answers.addAll(answers); } - boolean isEmpty() { + boolean isSet() { return answers.isEmpty(); } diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 5aef462cff..1eae21ceae 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -102,7 +102,7 @@ public void setAnswersForStubbing(List> answers, Strictness strictness } public boolean hasAnswersForStubbing() { - return !doAnswerStyleStubbing.isEmpty(); + return !doAnswerStyleStubbing.isSet(); } public boolean hasInvocationForPotentialStubbing() { From cc8271b0109f3242a4c1610632db920354b18888 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:06:33 -0800 Subject: [PATCH 20/50] Implemented doThrow syntax for lenient() mode --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index ed0968b91e..e292e39376 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -22,7 +22,7 @@ public Stubber doThrow(Throwable... toBeThrown) { @Override public Stubber doThrow(Class toBeThrown) { - return null; + return stubber().doThrow(toBeThrown); } @Override diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 800ae71124..c207c57050 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -85,6 +85,34 @@ public void doReturn_varargs_syntax() { assertEquals("3", mock.simpleMethod(1)); } + @Test + public void doThrow_syntax() { + //when + lenient() + .doThrow(IllegalArgumentException.class) + .doThrow(IllegalStateException.class) + .when(mock).simpleMethod(1); + + //then on lenient stubbing, we can call it with different argument with no exception: + mock.simpleMethod(200); + + //and stubbing works, too: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalArgumentException.class); + + //testing consecutive call: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalStateException.class); + } + static class Counter { int increment(int x) { return x + 1; From e3d77c7cb21eb5e3cc8f240f2e9cab3273a3a515 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:08:12 -0800 Subject: [PATCH 21/50] Implemented doThrow varag syntax for lenient() mode --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index e292e39376..1ea348a488 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -27,7 +27,7 @@ public Stubber doThrow(Class toBeThrown) { @Override public Stubber doThrow(Class toBeThrown, Class... nextToBeThrown) { - return null; + return stubber().doThrow(toBeThrown, nextToBeThrown); } @Override diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index c207c57050..e4aafb4f91 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -98,7 +98,6 @@ public void doThrow_syntax() { //and stubbing works, too: assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override public void call() throws Throwable { mock.simpleMethod(1); } @@ -106,7 +105,31 @@ public void call() throws Throwable { //testing consecutive call: assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalStateException.class); + } + + @Test + public void doThrow_vararg_syntax() { + //when + lenient() + .doThrow(IllegalArgumentException.class, IllegalStateException.class) + .when(mock).simpleMethod(1); + + //then on lenient stubbing, we can call it with different argument with no exception: + mock.simpleMethod(200); + + //and stubbing works, too: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalArgumentException.class); + + //testing consecutive call: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { public void call() throws Throwable { mock.simpleMethod(1); } From b2ba655db15347826c13b909d7ee171b8119ea63 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:13:57 -0800 Subject: [PATCH 22/50] Implemented doThrow instance varag syntax for lenient() mode --- .../stubbing/DefaultLenientStubber.java | 2 +- .../strictness/StrictnessPerStubbingTest.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java index 1ea348a488..0f3fe073a1 100644 --- a/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java +++ b/src/main/java/org/mockito/internal/stubbing/DefaultLenientStubber.java @@ -17,7 +17,7 @@ public class DefaultLenientStubber implements LenientStubber { @Override public Stubber doThrow(Throwable... toBeThrown) { - return null; + return stubber().doThrow(toBeThrown); } @Override diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index e4aafb4f91..504229c78e 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -136,6 +136,31 @@ public void call() throws Throwable { }).isInstanceOf(IllegalStateException.class); } + @Test + public void doThrow_instance_vararg_syntax() { + //when + lenient() + .doThrow(new IllegalArgumentException(), new IllegalStateException()) + .when(mock).simpleMethod(1); + + //then on lenient stubbing, we can call it with different argument with no exception: + mock.simpleMethod(200); + + //and stubbing works, too: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalArgumentException.class); + + //testing consecutive call: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + public void call() throws Throwable { + mock.simpleMethod(1); + } + }).isInstanceOf(IllegalStateException.class); + } + static class Counter { int increment(int x) { return x + 1; From a2f598bd5ce6b3dd70e1dbab4039fe78140e2549 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:25:15 -0800 Subject: [PATCH 23/50] Implemented lenient() support for JUnitRunner --- .../internal/junit/UnusedStubbingsFinder.java | 2 +- .../junit/UnusedStubbingsFinderTest.java | 1 + .../StrictnessPerStubbingWithRunnerTest.java | 51 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRunnerTest.java diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 2fbc0a7030..85bfd1d7ea 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -58,7 +58,7 @@ public Collection getUnusedStubbingsByLocation(Iterable mock //note that those are _not_ locations where the stubbings was used Set locationsOfUsedStubbings = new HashSet(); for (Stubbing s : stubbings) { - if (s.wasUsed()) { + if (s.wasUsed() || s.getStrictness() == Strictness.LENIENT) { String location = s.getInvocation().getLocation().toString(); locationsOfUsedStubbings.add(location); } diff --git a/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java b/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java index 63f23d0aa0..0eb3d7d346 100644 --- a/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java +++ b/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java @@ -108,6 +108,7 @@ public void some_unused_stubbings_by_location() throws Exception { when(mock1.simpleMethod(1)).thenReturn("1"); when(mock2.simpleMethod(2)).thenReturn("2"); when(mock2.simpleMethod(3)).thenReturn("3"); + lenient().when(mock2.differentMethod()).thenReturn("4"); //will not be included in results mock2.simpleMethod(2); diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRunnerTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRunnerTest.java new file mode 100644 index 0000000000..e42d340003 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRunnerTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockitousage.strictness; + +import org.assertj.core.api.ThrowableAssert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockitousage.IMethods; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class StrictnessPerStubbingWithRunnerTest { + + @Mock IMethods mock; + + @Test + public void potential_stubbing_problem() { + //when + when(mock.simpleMethod("1")).thenReturn("1"); + lenient().when(mock.differentMethod("2")).thenReturn("2"); + + //then on lenient stubbing, we can call it with different argument: + mock.differentMethod("200"); + + //but on strict stubbing, we cannot: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod("100"); + } + }).isInstanceOf(PotentialStubbingProblem.class); + + //let's use the strict stubbing so that it is not reported as failure by the runner: + mock.simpleMethod("1"); + } + + @Test + public void unnecessary_stubbing() { + //this unnecessary stubbing is not flagged by the runner: + lenient().when(mock.differentMethod("2")).thenReturn("2"); + } +} From 7434568a5f0ad11d4bb72e58c3196ef0774a16b1 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:35:47 -0800 Subject: [PATCH 24/50] Added coverage for using lenient() with JUnit Rules --- .../StrictnessPerStubbingWithRuleTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java new file mode 100644 index 0000000000..b5ad94b40d --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockitousage.strictness; + +import org.assertj.core.api.ThrowableAssert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +public class StrictnessPerStubbingWithRuleTest { + + @Mock IMethods mock; + @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); + + @Test + public void potential_stubbing_problem() { + //when + when(mock.simpleMethod("1")).thenReturn("1"); + lenient().when(mock.differentMethod("2")).thenReturn("2"); + + //then on lenient stubbing, we can call it with different argument: + mock.differentMethod("200"); + + //but on strict stubbing, we cannot: + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod("100"); + } + }).isInstanceOf(PotentialStubbingProblem.class); + + //let's use the strict stubbing so that it is not reported as failure by the rule: + mock.simpleMethod("1"); + } + + @Test + public void unnecessary_stubbing() { + //this unnecessary stubbing is not flagged by the rule: + lenient().when(mock.differentMethod("2")).thenReturn("2"); + } +} From fd42e6e27df7f40537df67ffbeea855c510b9100 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Mon, 15 Jan 2018 17:55:04 -0800 Subject: [PATCH 25/50] Removed cleared TODOs. --- .../java/org/mockito/internal/junit/UnusedStubbingsFinder.java | 1 - .../java/org/mockito/internal/stubbing/OngoingStubbingImpl.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 85bfd1d7ea..76423ecf57 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -33,7 +33,6 @@ public UnusedStubbings getUnusedStubbings(Iterable mocks) { List unused = filter(stubbings, new Filter() { public boolean isOut(Stubbing s) { - //TODO 792, Strictness.LENIENT -> we want to do the same thing for getUnusedStubbingsByLocation() return s.wasUsed() || s.getStrictness() == Strictness.LENIENT; } }); diff --git a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java index 1bf2414bcb..cd27c392dd 100644 --- a/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/OngoingStubbingImpl.java @@ -24,7 +24,6 @@ public OngoingStubbingImpl(InvocationContainerImpl invocationContainer) { @Override public OngoingStubbing thenAnswer(Answer answer) { - //TODO 792 rename all iOngoingStubbing -> ongoingStubbing if(!invocationContainer.hasInvocationForPotentialStubbing()) { throw incorrectUseOfApi(); } From 88092d8c138e6a3df35a1823f3b9a00c88b284c1 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 09:24:29 -0800 Subject: [PATCH 26/50] Improved coverage. The mock needs to be created after we start the session. This way, we guarantee that the session does not update mock's strictness. --- .../org/mockitousage/strictness/StrictnessPerMockTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java index 37c8071dcb..6e9d001261 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -20,6 +20,7 @@ import org.mockitousage.IMethods; import org.mockitoutil.TestBase; +import static org.junit.Assert.assertNull; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -30,11 +31,13 @@ public class StrictnessPerMockTest { MockitoSession mockito; @Mock IMethods strictStubsMock; - IMethods lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); + IMethods lenientMock; @Before public void before() { mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); + assertNull(lenientMock); + lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); } @Test @@ -92,7 +95,7 @@ public void call() throws Throwable { verifyNoMoreInteractions(strictStubsMock, lenientMock); } }).isInstanceOf(NoInteractionsWanted.class) - .hasMessageContaining("But found this interaction on mock 'lenientMock'") + .hasMessageContaining("But found this interaction on mock 'iMethods'") //TODO 792: let's make NoInteractionsWanted exception contain the Invocation instances //so that we can write clean assertion rather than depending on string .hasMessageContaining("Actually, above is the only interaction with this mock"); From d7cf98ea0ec2e1f2936aa186ba14208d3ef8d1a1 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 09:29:29 -0800 Subject: [PATCH 27/50] Added TODO --- src/main/java/org/mockito/MockSettings.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index d8753fa79a..740e7d663d 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -318,5 +318,6 @@ public interface MockSettings extends Serializable { @Incubating MockCreationSettings build(Class typeToMock); + //TODO 792 let's just do lenient() for simpler API? MockSettings strictness(Strictness strictness); } From ec24ce1ae5f9ce148ac1d1a2c45e23dbd9cfe202 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 10:00:45 -0800 Subject: [PATCH 28/50] Added coverage --- .../mockitousage/strictness/StrictnessPerMockTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java index 6e9d001261..e7622f9531 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -20,9 +20,11 @@ import org.mockitousage.IMethods; import org.mockitoutil.TestBase; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.withSettings; @@ -40,6 +42,12 @@ public void before() { lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); } + @Test + public void knows_mock_strictness() { + assertEquals(Strictness.LENIENT, mockingDetails(lenientMock).getMockCreationSettings().getStrictness()); + assertEquals(null, mockingDetails(strictStubsMock).getMockCreationSettings().getStrictness()); + } + @Test public void potential_stubbing_problem() { //when From b9bf63ec9c509d42c485a648c7008a2bc269947d Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 10:02:10 -0800 Subject: [PATCH 29/50] Refactoring and tidy up --- .../internal/handler/MockHandlerImpl.java | 2 ++ .../junit/DefaultStubbingLookupListener.java | 10 ++++------ .../internal/junit/UnusedStubbingsFinder.java | 1 + .../internal/stubbing/StrictnessSelector.java | 20 +++++++++++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 67fe948b03..7462815eaa 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -135,6 +135,8 @@ private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingF //TODO #793 - when completed, we should be able to get rid of the casting below List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { + //TODO 792 pass this.invocationContainer.getStubbedInvocations() so that we don't have to call 'mockingDetails' + // each time a method is called on a mock (slow). listener.onStubbingLookup(invocation, stubbingFound, mockSettings); } } diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 87cc5bd226..7dc145b38d 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -6,6 +6,7 @@ import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.listeners.StubbingLookupListener; +import org.mockito.internal.stubbing.StrictnessSelector; import org.mockito.invocation.Invocation; import org.mockito.mock.MockCreationSettings; import org.mockito.quality.Strictness; @@ -31,13 +32,9 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { } public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { - //TODO this is not quite right - if (currentStrictness != Strictness.STRICT_STUBS) { - return; - } + Strictness actualStrictness = StrictnessSelector.determineStrictness(currentStrictness, mockSettings, stubbingFound); - if (mockSettings.getStrictness() == Strictness.LENIENT || mockSettings.getStrictness() == Strictness.WARN) { - //strictness explicitly relaxed at the mock level + if (actualStrictness != Strictness.STRICT_STUBS) { return; } @@ -61,6 +58,7 @@ private static List potentialArgMismatches(Invocation invocation) { Collection stubbings = mockingDetails(invocation.getMock()).getStubbings(); for (Stubbing s : stubbings) { if (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName()) + //TODO 792 - do we need to have the strictness check here? && s.getStrictness() != Strictness.LENIENT) { matchingStubbings.add(s.getInvocation()); } diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 76423ecf57..13490e6600 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -33,6 +33,7 @@ public UnusedStubbings getUnusedStubbings(Iterable mocks) { List unused = filter(stubbings, new Filter() { public boolean isOut(Stubbing s) { + //TODO 792 this should be (and below, too) && s.getStrictness() == Strictness.STRICT_STUBS; return s.wasUsed() || s.getStrictness() == Strictness.LENIENT; } }); diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java new file mode 100644 index 0000000000..b7320d35c0 --- /dev/null +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -0,0 +1,20 @@ +package org.mockito.internal.stubbing; + +import org.mockito.mock.MockCreationSettings; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Stubbing; + +public class StrictnessSelector { + + public static Strictness determineStrictness(Strictness currentStrictness, MockCreationSettings mockSettings, Stubbing stubbing) { + if (stubbing != null && stubbing.getStrictness() != null) { + return stubbing.getStrictness(); + } + + if (mockSettings.getStrictness() != null) { + return mockSettings.getStrictness(); + } + + return currentStrictness; + } +} From 61403ea3c0db46b1775e31ca55bc5153726a195b Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 10:10:52 -0800 Subject: [PATCH 30/50] Removed a TODO after verification, added documentation --- .../internal/junit/DefaultStubbingLookupListener.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 7dc145b38d..90297201cb 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -6,7 +6,6 @@ import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.listeners.StubbingLookupListener; -import org.mockito.internal.stubbing.StrictnessSelector; import org.mockito.invocation.Invocation; import org.mockito.mock.MockCreationSettings; import org.mockito.quality.Strictness; @@ -17,6 +16,7 @@ import java.util.List; import static org.mockito.Mockito.mockingDetails; +import static org.mockito.internal.stubbing.StrictnessSelector.determineStrictness; /** * Default implementation of stubbing lookup listener. @@ -32,7 +32,7 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { } public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { - Strictness actualStrictness = StrictnessSelector.determineStrictness(currentStrictness, mockSettings, stubbingFound); + Strictness actualStrictness = determineStrictness(currentStrictness, mockSettings, stubbingFound); if (actualStrictness != Strictness.STRICT_STUBS) { return; @@ -58,7 +58,8 @@ private static List potentialArgMismatches(Invocation invocation) { Collection stubbings = mockingDetails(invocation.getMock()).getStubbings(); for (Stubbing s : stubbings) { if (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName()) - //TODO 792 - do we need to have the strictness check here? + //in case the mock is strict but the stubbing is lenient, + // we don't want to report lenient stubbing as potential arg mismatch && s.getStrictness() != Strictness.LENIENT) { matchingStubbings.add(s.getInvocation()); } From 2edf0ce97f25aa879a00b3fc360779a8cbbef3d0 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 17 Jan 2018 10:14:42 -0800 Subject: [PATCH 31/50] Added a TODO --- .../java/org/mockito/internal/stubbing/StubbingStrictness.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java b/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java index 4878a331b3..fd8611e08a 100644 --- a/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java +++ b/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java @@ -10,6 +10,8 @@ import static org.mockito.Mockito.mockingDetails; public class StubbingStrictness { + //TODO 792 - this does not seem correct - we don't check the leniency of the stubbing itself, only mock-level settings + //we should also check if we can pass mock creation settings or mocking details directly to avoid 'mockingDetails()' method overhead public static boolean isLenientStubbing(Stubbing stubbing) { Strictness mockStrictness = mockingDetails(stubbing.getInvocation().getMock()).getMockCreationSettings().getStrictness(); return mockStrictness == Strictness.LENIENT; From d4d12abca4e376cc92f20b970ccd7461cbe58286 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 20 Jan 2018 18:37:52 -0800 Subject: [PATCH 32/50] Added more sanity coverage --- .../strictness/StrictnessPerStubbingTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 504229c78e..6a6542e617 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -232,6 +232,15 @@ public void call() throws Throwable { .isNot(TestBase.hasMessageContaining("2. ->")); } + @Test + public void unnecessary_stubbing_with_doReturn() { + //when + lenient().doReturn("2").when(mock).differentMethod("2"); + + //then no exception is thrown: + mockito.finishMocking(); + } + @Test public void verify_no_more_invocations() { //when From 81508672b727bfd979d7debf4e067d3a3d20614e Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 20 Jan 2018 18:38:54 -0800 Subject: [PATCH 33/50] Refactoring: placed stubbing evaluation code in the same spot --- .../internal/junit/UnusedStubbings.java | 7 ++----- .../stubbing/DoAnswerStyleStubbing.java | 12 ++++++------ .../stubbing/InvocationContainerImpl.java | 16 ++++++++++------ .../internal/stubbing/StubbingStrictness.java | 19 ------------------- 4 files changed, 18 insertions(+), 36 deletions(-) delete mode 100644 src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbings.java b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java index d262dccc9c..3505d2cb1c 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbings.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java @@ -5,7 +5,6 @@ package org.mockito.internal.junit; import org.mockito.internal.exceptions.Reporter; -import org.mockito.internal.stubbing.StubbingStrictness; import org.mockito.internal.util.MockitoLogger; import org.mockito.invocation.Invocation; import org.mockito.stubbing.Stubbing; @@ -48,16 +47,14 @@ public String toString() { return unused.toString(); } - public void reportUnused() { + void reportUnused() { if (unused.isEmpty()) { return; } List invocations = new LinkedList(); for (Stubbing stubbing : unused) { - if (!StubbingStrictness.isLenientStubbing(stubbing)) { - invocations.add(stubbing.getInvocation()); - } + invocations.add(stubbing.getInvocation()); } if (invocations.isEmpty()) { return; diff --git a/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java index d10add4903..56491b8577 100644 --- a/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java +++ b/src/main/java/org/mockito/internal/stubbing/DoAnswerStyleStubbing.java @@ -17,10 +17,10 @@ class DoAnswerStyleStubbing implements Serializable { private final List> answers = new ArrayList>(); - private Strictness strictness; + private Strictness stubbingStrictness; - void setAnswers(List> answers, Strictness strictness) { - this.strictness = strictness; + void setAnswers(List> answers, Strictness stubbingStrictness) { + this.stubbingStrictness = stubbingStrictness; this.answers.addAll(answers); } @@ -30,14 +30,14 @@ boolean isSet() { void clear() { answers.clear(); - strictness = null; + stubbingStrictness = null; } List> getAnswers() { return answers; } - Strictness getStrictness() { - return strictness; + Strictness getStubbingStrictness() { + return stubbingStrictness; } } diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 1eae21ceae..838864714c 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -28,13 +28,16 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl private static final long serialVersionUID = -5334301962749537177L; private final LinkedList stubbed = new LinkedList(); - private final DoAnswerStyleStubbing doAnswerStyleStubbing = new DoAnswerStyleStubbing(); + private final DoAnswerStyleStubbing doAnswerStyleStubbing; private final RegisteredInvocations registeredInvocations; + private final Strictness mockStrictness; private MatchableInvocation invocationForStubbing; public InvocationContainerImpl(MockCreationSettings mockSettings) { this.registeredInvocations = createRegisteredInvocations(mockSettings); + this.mockStrictness = mockSettings.getStrictness(); + this.doAnswerStyleStubbing = new DoAnswerStyleStubbing(); } public void setInvocationForPotentialStubbing(MatchableInvocation invocation) { @@ -46,9 +49,9 @@ public void resetInvocationForPotentialStubbing(MatchableInvocation invocationMa this.invocationForStubbing = invocationMatcher; } - public void addAnswer(Answer answer, Strictness strictness) { + public void addAnswer(Answer answer, Strictness stubbingStrictness) { registeredInvocations.removeLast(); - addAnswer(answer, false, strictness); + addAnswer(answer, false, stubbingStrictness); } public void addConsecutiveAnswer(Answer answer) { @@ -58,7 +61,7 @@ public void addConsecutiveAnswer(Answer answer) { /** * Adds new stubbed answer and returns the invocation matcher the answer was added to. */ - public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness strictness) { + public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness stubbingStrictness) { Invocation invocation = invocationForStubbing.getInvocation(); mockingProgress().stubbingCompleted(); if (answer instanceof ValidableAnswer) { @@ -69,7 +72,8 @@ public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, if (isConsecutive) { stubbed.getFirst().addAnswer(answer); } else { - stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, strictness)); + Strictness effectiveStrictness = stubbingStrictness != null ? stubbingStrictness : this.mockStrictness; + stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, effectiveStrictness)); } return stubbed.getFirst(); } @@ -113,7 +117,7 @@ public void setMethodForStubbing(MatchableInvocation invocation) { invocationForStubbing = invocation; assert hasAnswersForStubbing(); for (int i = 0; i < doAnswerStyleStubbing.getAnswers().size(); i++) { - addAnswer(doAnswerStyleStubbing.getAnswers().get(i), i != 0, doAnswerStyleStubbing.getStrictness()); + addAnswer(doAnswerStyleStubbing.getAnswers().get(i), i != 0, doAnswerStyleStubbing.getStubbingStrictness()); } doAnswerStyleStubbing.clear(); } diff --git a/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java b/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java deleted file mode 100644 index fd8611e08a..0000000000 --- a/src/main/java/org/mockito/internal/stubbing/StubbingStrictness.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2007 Mockito contributors - * This program is made available under the terms of the MIT License. - */ -package org.mockito.internal.stubbing; - -import org.mockito.quality.Strictness; -import org.mockito.stubbing.Stubbing; - -import static org.mockito.Mockito.mockingDetails; - -public class StubbingStrictness { - //TODO 792 - this does not seem correct - we don't check the leniency of the stubbing itself, only mock-level settings - //we should also check if we can pass mock creation settings or mocking details directly to avoid 'mockingDetails()' method overhead - public static boolean isLenientStubbing(Stubbing stubbing) { - Strictness mockStrictness = mockingDetails(stubbing.getInvocation().getMock()).getMockCreationSettings().getStrictness(); - return mockStrictness == Strictness.LENIENT; - } -} From 8e237472e1b912b2c683281d088dc8b871c68d61 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 20 Jan 2018 18:46:10 -0800 Subject: [PATCH 34/50] Added more coverage for edge cases --- ...Test.java => StrictnessWithRulesTest.java} | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) rename src/test/java/org/mockitousage/strictness/{StrictnessPerStubbingWithRuleTest.java => StrictnessWithRulesTest.java} (66%) diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java b/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java similarity index 66% rename from src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java rename to src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java index b5ad94b40d..2a2fe0172a 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingWithRuleTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java @@ -17,9 +17,11 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; -public class StrictnessPerStubbingWithRuleTest { +public class StrictnessWithRulesTest { @Mock IMethods mock; @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS); @@ -50,4 +52,24 @@ public void unnecessary_stubbing() { //this unnecessary stubbing is not flagged by the rule: lenient().when(mock.differentMethod("2")).thenReturn("2"); } + + @Test + public void rule_changes_strictness() { + //when + rule.strictness(Strictness.LENIENT); + + //then original mock is lenient: + when(mock.simpleMethod(1)).thenReturn("1"); + mock.simpleMethod(2); + + //but the new strict-stubs mock is strict, even though the rule is not: + final IMethods strictStubsMock = mock(IMethods.class, withSettings().strictness(Strictness.STRICT_STUBS)); + when(strictStubsMock.simpleMethod(1)).thenReturn("1"); + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + strictStubsMock.simpleMethod(2); + } + }); + } } From 08a9b9cdb42033f1fcc1fba9dabc9c3f024960c5 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 20 Jan 2018 19:20:12 -0800 Subject: [PATCH 35/50] Simplified public API for now We don't need Strictness per mock just yet. Let's offer our users simple 'lenient()' setting on the mock. --- src/main/java/org/mockito/MockSettings.java | 5 +- .../internal/creation/MockSettingsImpl.java | 5 +- .../creation/settings/CreationSettings.java | 9 ++- .../stubbing/InvocationContainerImpl.java | 2 +- .../internal/stubbing/StrictnessSelector.java | 4 +- .../mockito/mock/MockCreationSettings.java | 4 +- .../strictness/StrictnessPerMockTest.java | 11 +-- ...ctnessWhenRuleStrictnessIsUpdatedTest.java | 67 +++++++++++++++++++ .../strictness/StrictnessWithRulesTest.java | 22 ------ 9 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 src/test/java/org/mockitousage/strictness/StrictnessWhenRuleStrictnessIsUpdatedTest.java diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index 740e7d663d..c060dc1c68 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -10,7 +10,6 @@ import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.SerializableMode; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -318,6 +317,6 @@ public interface MockSettings extends Serializable { @Incubating MockCreationSettings build(Class typeToMock); - //TODO 792 let's just do lenient() for simpler API? - MockSettings strictness(Strictness strictness); + @Incubating + MockSettings lenient(); } diff --git a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java index c28599d3a4..ca67730793 100644 --- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java +++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java @@ -15,7 +15,6 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -228,8 +227,8 @@ public MockCreationSettings build(Class typeToMock) { } @Override - public MockSettings strictness(Strictness strictness) { - this.strictness = strictness; + public MockSettings lenient() { + this.lenient = true; 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 1d01f1a123..03afd80947 100644 --- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java +++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java @@ -10,7 +10,6 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.mock.MockName; import org.mockito.mock.SerializableMode; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -38,7 +37,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl private boolean useConstructor; private Object outerClassInstance; private Object[] constructorArgs; - protected Strictness strictness; + protected boolean lenient; public CreationSettings() {} @@ -57,7 +56,7 @@ public CreationSettings(CreationSettings copy) { this.useConstructor = copy.isUsingConstructor(); this.outerClassInstance = copy.getOuterClassInstance(); this.constructorArgs = copy.getConstructorArgs(); - this.strictness = copy.strictness; + this.lenient = copy.lenient; } @Override @@ -158,7 +157,7 @@ public boolean isStubOnly() { } @Override - public Strictness getStrictness() { - return strictness; + public boolean isLenient() { + return lenient; } } diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 838864714c..d34a70b5e5 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -36,7 +36,7 @@ public class InvocationContainerImpl implements InvocationContainer, Serializabl public InvocationContainerImpl(MockCreationSettings mockSettings) { this.registeredInvocations = createRegisteredInvocations(mockSettings); - this.mockStrictness = mockSettings.getStrictness(); + this.mockStrictness = mockSettings.isLenient() ? Strictness.LENIENT : null; this.doAnswerStyleStubbing = new DoAnswerStyleStubbing(); } diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java index b7320d35c0..704f741111 100644 --- a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -11,8 +11,8 @@ public static Strictness determineStrictness(Strictness currentStrictness, MockC return stubbing.getStrictness(); } - if (mockSettings.getStrictness() != null) { - return mockSettings.getStrictness(); + if (mockSettings.isLenient()) { + return Strictness.LENIENT; } return currentStrictness; diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index d555143c67..61c6e0b142 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -9,7 +9,6 @@ import org.mockito.NotExtensible; import org.mockito.listeners.InvocationListener; import org.mockito.listeners.VerificationStartedListener; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.List; @@ -111,5 +110,6 @@ public interface MockCreationSettings { @Incubating Object getOuterClassInstance(); - Strictness getStrictness(); + @Incubating + boolean isLenient(); } diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java index e7622f9531..2dce3e05e4 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java @@ -20,7 +20,8 @@ import org.mockitousage.IMethods; import org.mockitoutil.TestBase; -import static org.junit.Assert.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertNull; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -39,13 +40,13 @@ public class StrictnessPerMockTest { public void before() { mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking(); assertNull(lenientMock); - lenientMock = mock(IMethods.class, withSettings().strictness(Strictness.LENIENT)); + lenientMock = mock(IMethods.class, withSettings().lenient()); } @Test - public void knows_mock_strictness() { - assertEquals(Strictness.LENIENT, mockingDetails(lenientMock).getMockCreationSettings().getStrictness()); - assertEquals(null, mockingDetails(strictStubsMock).getMockCreationSettings().getStrictness()); + public void knows_if_mock_is_lenient() { + assertTrue(mockingDetails(lenientMock).getMockCreationSettings().isLenient()); + assertFalse(mockingDetails(strictStubsMock).getMockCreationSettings().isLenient()); } @Test diff --git a/src/test/java/org/mockitousage/strictness/StrictnessWhenRuleStrictnessIsUpdatedTest.java b/src/test/java/org/mockitousage/strictness/StrictnessWhenRuleStrictnessIsUpdatedTest.java new file mode 100644 index 0000000000..592cca9949 --- /dev/null +++ b/src/test/java/org/mockitousage/strictness/StrictnessWhenRuleStrictnessIsUpdatedTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ + +package org.mockitousage.strictness; + +import org.assertj.core.api.ThrowableAssert; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.quality.Strictness; +import org.mockitousage.IMethods; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + +public class StrictnessWhenRuleStrictnessIsUpdatedTest { + + @Mock IMethods mock; + @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.LENIENT); + + @Test + public void strictness_per_mock() { + //when + rule.strictness(Strictness.STRICT_STUBS); + + //then previous mock is strict: + when(mock.simpleMethod(1)).thenReturn("1"); + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(2); + } + }).isInstanceOf(PotentialStubbingProblem.class); + + //but the new mock is lenient, even though the rule is not: + final IMethods lenientMock = mock(IMethods.class, withSettings().lenient()); + when(lenientMock.simpleMethod(1)).thenReturn("1"); + lenientMock.simpleMethod(100); + } + + @Test + public void strictness_per_stubbing() { + //when + rule.strictness(Strictness.STRICT_STUBS); + + //then previous mock is strict: + when(mock.simpleMethod(1)).thenReturn("1"); + assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { + @Override + public void call() throws Throwable { + mock.simpleMethod(2); + } + }).isInstanceOf(PotentialStubbingProblem.class); + + //but the new mock is lenient, even though the rule is not: + lenient().when(mock.simpleMethod(1)).thenReturn("1"); + mock.simpleMethod(100); + } +} diff --git a/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java b/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java index 2a2fe0172a..3a323fb2f4 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.java @@ -17,9 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; public class StrictnessWithRulesTest { @@ -52,24 +50,4 @@ public void unnecessary_stubbing() { //this unnecessary stubbing is not flagged by the rule: lenient().when(mock.differentMethod("2")).thenReturn("2"); } - - @Test - public void rule_changes_strictness() { - //when - rule.strictness(Strictness.LENIENT); - - //then original mock is lenient: - when(mock.simpleMethod(1)).thenReturn("1"); - mock.simpleMethod(2); - - //but the new strict-stubs mock is strict, even though the rule is not: - final IMethods strictStubsMock = mock(IMethods.class, withSettings().strictness(Strictness.STRICT_STUBS)); - when(strictStubsMock.simpleMethod(1)).thenReturn("1"); - assertThatThrownBy(new ThrowableAssert.ThrowingCallable() { - @Override - public void call() throws Throwable { - strictStubsMock.simpleMethod(2); - } - }); - } } From 7bcc5486d86002274775b7bba05db2929534784d Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sun, 21 Jan 2018 14:24:24 -0800 Subject: [PATCH 36/50] Checkstyle --- .../org/mockito/internal/stubbing/StrictnessSelector.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java index 704f741111..eef4a0d4e4 100644 --- a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -1,3 +1,7 @@ +/* + * Copyright (c) 2007 Mockito contributors + * This program is made available under the terms of the MIT License. + */ package org.mockito.internal.stubbing; import org.mockito.mock.MockCreationSettings; From da0481fd74a2f49bfe364d420ceecce774be6a4d Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 27 Jan 2018 20:12:39 -0800 Subject: [PATCH 37/50] Added TODOs to help organization --- src/main/java/org/mockito/MockSettings.java | 2 +- src/main/java/org/mockito/Mockito.java | 1 + src/main/java/org/mockito/internal/handler/MockHandlerImpl.java | 2 +- .../java/org/mockito/internal/junit/UnusedStubbingsFinder.java | 1 - .../java/org/mockito/internal/stubbing/StrictnessSelector.java | 1 + src/main/java/org/mockito/mock/MockCreationSettings.java | 2 +- src/main/java/org/mockito/stubbing/Stubbing.java | 2 ++ .../org/mockitousage/strictness/StrictnessPerStubbingTest.java | 1 - 8 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index c060dc1c68..ea7d62f526 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -317,6 +317,6 @@ public interface MockSettings extends Serializable { @Incubating MockCreationSettings build(Class typeToMock); - @Incubating + @Incubating//TODO X javadoc MockSettings lenient(); } diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index f503070839..200065997a 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -2952,6 +2952,7 @@ public static MockitoSessionBuilder mockitoSession() { return new DefaultMockitoSessionBuilder(); } + @Incubating //TODO x javadoc public static LenientStubber lenient() { return MOCKITO_CORE.lenient(); } diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 7462815eaa..48a181ce49 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -135,7 +135,7 @@ private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingF //TODO #793 - when completed, we should be able to get rid of the casting below List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { - //TODO 792 pass this.invocationContainer.getStubbedInvocations() so that we don't have to call 'mockingDetails' + //TODO x pass this.invocationContainer.getStubbedInvocations() so that we don't have to call 'mockingDetails' // each time a method is called on a mock (slow). listener.onStubbingLookup(invocation, stubbingFound, mockSettings); } diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 13490e6600..76423ecf57 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -33,7 +33,6 @@ public UnusedStubbings getUnusedStubbings(Iterable mocks) { List unused = filter(stubbings, new Filter() { public boolean isOut(Stubbing s) { - //TODO 792 this should be (and below, too) && s.getStrictness() == Strictness.STRICT_STUBS; return s.wasUsed() || s.getStrictness() == Strictness.LENIENT; } }); diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java index eef4a0d4e4..43a2d583c8 100644 --- a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -10,6 +10,7 @@ public class StrictnessSelector { + //TODO x explain public static Strictness determineStrictness(Strictness currentStrictness, MockCreationSettings mockSettings, Stubbing stubbing) { if (stubbing != null && stubbing.getStrictness() != null) { return stubbing.getStrictness(); diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index 61c6e0b142..bebdd66a9e 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -110,6 +110,6 @@ public interface MockCreationSettings { @Incubating Object getOuterClassInstance(); - @Incubating + @Incubating//TODO x javadoc boolean isLenient(); } diff --git a/src/main/java/org/mockito/stubbing/Stubbing.java b/src/main/java/org/mockito/stubbing/Stubbing.java index 707ef66cff..09e87bc444 100644 --- a/src/main/java/org/mockito/stubbing/Stubbing.java +++ b/src/main/java/org/mockito/stubbing/Stubbing.java @@ -4,6 +4,7 @@ */ package org.mockito.stubbing; +import org.mockito.Incubating; import org.mockito.MockingDetails; import org.mockito.NotExtensible; import org.mockito.invocation.Invocation; @@ -52,5 +53,6 @@ public interface Stubbing extends Answer { */ boolean wasUsed(); + @Incubating//TODO x javadoc Strictness getStrictness(); } diff --git a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java index 6a6542e617..3838beef7a 100644 --- a/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java +++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java @@ -42,7 +42,6 @@ public void before() { public void potential_stubbing_problem() { //when when(mock.simpleMethod("1")).thenReturn("1"); - //TODO 792: consider renaming lenient() -> optional() lenient().when(mock.differentMethod("2")).thenReturn("2"); //then on lenient stubbing, we can call it with different argument: From d77765088bdc747f25b30cf34fa9cbb4544ae35a Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sun, 28 Jan 2018 18:36:44 -0800 Subject: [PATCH 38/50] Reduced conditional duplication Based on code review feedback. --- .../junit/DefaultStubbingLookupListener.java | 7 +++--- .../internal/junit/UnusedStubbingsFinder.java | 6 ++--- .../stubbing/UnusedStubbingReporting.java | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 src/main/java/org/mockito/internal/stubbing/UnusedStubbingReporting.java diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 90297201cb..2b4b21bf99 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -6,6 +6,7 @@ import org.mockito.internal.exceptions.Reporter; import org.mockito.internal.listeners.StubbingLookupListener; +import org.mockito.internal.stubbing.UnusedStubbingReporting; import org.mockito.invocation.Invocation; import org.mockito.mock.MockCreationSettings; import org.mockito.quality.Strictness; @@ -57,10 +58,8 @@ private static List potentialArgMismatches(Invocation invocation) { List matchingStubbings = new LinkedList(); Collection stubbings = mockingDetails(invocation.getMock()).getStubbings(); for (Stubbing s : stubbings) { - if (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName()) - //in case the mock is strict but the stubbing is lenient, - // we don't want to report lenient stubbing as potential arg mismatch - && s.getStrictness() != Strictness.LENIENT) { + if (UnusedStubbingReporting.shouldBeReported(s) + && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName())) { matchingStubbings.add(s.getInvocation()); } } diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java index 76423ecf57..14c61c5578 100644 --- a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java +++ b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java @@ -5,9 +5,9 @@ package org.mockito.internal.junit; import org.mockito.internal.invocation.finder.AllInvocationsFinder; +import org.mockito.internal.stubbing.UnusedStubbingReporting; import org.mockito.internal.util.collections.ListUtil.Filter; import org.mockito.invocation.Invocation; -import org.mockito.quality.Strictness; import org.mockito.stubbing.Stubbing; import java.util.Collection; @@ -33,7 +33,7 @@ public UnusedStubbings getUnusedStubbings(Iterable mocks) { List unused = filter(stubbings, new Filter() { public boolean isOut(Stubbing s) { - return s.wasUsed() || s.getStrictness() == Strictness.LENIENT; + return !UnusedStubbingReporting.shouldBeReported(s); } }); @@ -57,7 +57,7 @@ public Collection getUnusedStubbingsByLocation(Iterable mock //note that those are _not_ locations where the stubbings was used Set locationsOfUsedStubbings = new HashSet(); for (Stubbing s : stubbings) { - if (s.wasUsed() || s.getStrictness() == Strictness.LENIENT) { + if (!UnusedStubbingReporting.shouldBeReported(s)) { String location = s.getInvocation().getLocation().toString(); locationsOfUsedStubbings.add(location); } diff --git a/src/main/java/org/mockito/internal/stubbing/UnusedStubbingReporting.java b/src/main/java/org/mockito/internal/stubbing/UnusedStubbingReporting.java new file mode 100644 index 0000000000..6a96592453 --- /dev/null +++ b/src/main/java/org/mockito/internal/stubbing/UnusedStubbingReporting.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockito.internal.stubbing; + +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Stubbing; + +/** + * Helps determining if stubbing should be reported as unused + */ +public class UnusedStubbingReporting { + + /** + * Decides if the stubbing should be reported as unused. + * Lenient stubbings are not reported as unused. + */ + public static boolean shouldBeReported(Stubbing stubbing) { + return !stubbing.wasUsed() && stubbing.getStrictness() != Strictness.LENIENT; + } +} From d60108d72bc396c8b5a9a78e9cc4a22d2bfa7d19 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sun, 28 Jan 2018 18:49:06 -0800 Subject: [PATCH 39/50] Added Javadoc --- .../junit/DefaultStubbingLookupListener.java | 2 +- .../internal/stubbing/StrictnessSelector.java | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 2b4b21bf99..9b0b531c14 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -33,7 +33,7 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { } public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { - Strictness actualStrictness = determineStrictness(currentStrictness, mockSettings, stubbingFound); + Strictness actualStrictness = determineStrictness(stubbingFound, mockSettings, currentStrictness); if (actualStrictness != Strictness.STRICT_STUBS) { return; diff --git a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java index 43a2d583c8..6009e9340e 100644 --- a/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java +++ b/src/main/java/org/mockito/internal/stubbing/StrictnessSelector.java @@ -8,10 +8,24 @@ import org.mockito.quality.Strictness; import org.mockito.stubbing.Stubbing; +/** + * Helps determining the actual strictness given that it can be configured in multiple ways (at mock, at stubbing, in rule) + */ public class StrictnessSelector { - //TODO x explain - public static Strictness determineStrictness(Strictness currentStrictness, MockCreationSettings mockSettings, Stubbing stubbing) { + /** + * Determines the actual strictness in the following importance order: + * 1st - strictness configured when declaring stubbing; + * 2nd - strictness configured at mock level; + * 3rd - strictness configured at test level (rule, mockito session) + * + * @param stubbing stubbing to check for strictness. Null permitted. + * @param mockSettings settings of the mock object, may or may not have strictness configured. Must not be null. + * @param testLevelStrictness strictness configured using the test-level configuration (rule, mockito session). Null permitted. + * + * @return actual strictness, can be null. + */ + public static Strictness determineStrictness(Stubbing stubbing, MockCreationSettings mockSettings, Strictness testLevelStrictness) { if (stubbing != null && stubbing.getStrictness() != null) { return stubbing.getStrictness(); } @@ -20,6 +34,6 @@ public static Strictness determineStrictness(Strictness currentStrictness, MockC return Strictness.LENIENT; } - return currentStrictness; + return testLevelStrictness; } } From 99f6610ccf6043c99fd1684e338e8fb496848ccf Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 18 Jul 2018 20:55:46 +0200 Subject: [PATCH 40/50] Updated strictness documentation --- src/main/java/org/mockito/MockSettings.java | 62 ++++++++++++++++++- .../misusing/PotentialStubbingProblem.java | 21 ++----- .../UnnecessaryStubbingException.java | 8 ++- .../mockito/mock/MockCreationSettings.java | 10 ++- .../java/org/mockito/quality/MockitoHint.java | 7 ++- .../java/org/mockito/stubbing/Stubbing.java | 9 ++- 6 files changed, 93 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index ea7d62f526..a31ea8ecbe 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -4,12 +4,14 @@ */ package org.mockito; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; import org.mockito.invocation.InvocationFactory; import org.mockito.invocation.MockHandler; import org.mockito.listeners.InvocationListener; import org.mockito.listeners.VerificationStartedListener; import org.mockito.mock.MockCreationSettings; import org.mockito.mock.SerializableMode; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.Serializable; @@ -317,6 +319,64 @@ public interface MockSettings extends Serializable { @Incubating MockCreationSettings build(Class typeToMock); - @Incubating//TODO X javadoc + /** + * Mock will have {@link Strictness#LENIENT} strictness. + * Useful when all mocks of the test are stricter but you want a specific mock to be lenient. + *

+     *   Foo mock = mock(Foo.class, withSettings.lenient());
+     * 
+ *

+ * Most mocks in most tests should use strict stubbing ({@link Strictness#STRICT_STUBS} because it gives the best developer experience. + * There are rare scenarios where strict stubbing gives false negative signal: + *

    + *
  • detecting stubbing argument mismatch - see {@link PotentialStubbingProblem}
  • + *
  • detecting unused stubbings - see {@link PotentialStubbingProblem})
  • + *
+ * If you false negatives in any of those scenarios you can configure a specific mock to be lenient. + * If there is only one (few) stubbings that need to be lenient it is better to set the stubbing to be lenient, rather than the entire mock object. + * See {@link Mockito#lenient()}. + *

+ * In below example, 'foo.foo()' is a default stubbing that was moved to 'before()' method to avoid duplication. + * Doing so makes the 'test3()' method fail with unnecessary stubbing on 'foo.foo()'. + * To resolve it we can configure 'Foo' mock to be lenient or configure 'foo.foo()' stubbing to be lenient. + *

+ * Note that this example is contrived. + * It is not desired to eliminate all possible duplication from the test code + * because it may add complexity and conceal important test information. + * Some repetition in tests is OK, use your own judgement to write great tests! + * + *


+     * public class SomeTest {
+     *
+     *     @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(STRICT_STUBS);
+     *
+     *     @Mock Foo foo;
+     *     @Mock Bar bar;
+     *
+     *     @Before public void before() {
+     *         when(foo.foo()).thenReturn("ok");
+     *
+     *         // it is better to configure the stubbing to be lenient:
+     *         // lenient().when(foo.foo()).thenReturn("ok");
+     *
+     *         // or the entire mock to be lenient:
+     *         // foo = mock(Foo.class, withSettings().lenient());
+     *     }
+     *
+     *     @Test public void test1() {
+     *         foo.foo();
+     *     }
+     *
+     *     @Test public void test2() {
+     *         foo.foo();
+     *     }
+     *
+     *     @Test public void test3() {
+     *         bar.bar();
+     *     }
+     * }
+     * 
+ */ + @Incubating MockSettings lenient(); } diff --git a/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java b/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java index 42e990dab8..82ca9ed28c 100644 --- a/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java +++ b/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java @@ -4,6 +4,7 @@ */ package org.mockito.exceptions.misusing; +import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.quality.Strictness; import org.mockito.exceptions.base.MockitoException; @@ -53,22 +54,10 @@ * It is a well known limitation of Mockito API and another example how Mockito optimizes its clean API for 95% of the cases * while still supporting edge cases. * - *
  • Reduce the strictness level in the test method (only for JUnit Rules): - *
    
    - * public class ExampleTest {
    - *     @Rule
    - *     public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
    - *
    - *     @Test public void exampleTest() {
    - *         //Change the strictness level only for this test method:
    - *         mockito.strictness(Strictness.LENIENT);
    - *
    - *         //remaining test code
    - *     }
    - * }
    - * 
    - * Currently, reducing strictness is only available to JUnit rules. - * If you need it in a different context let us know at issue 857. + *
  • Reduce the strictness level. If you need one (few) stubbings to be lenient use {@link Mockito#lenient()}. + * If you want a single mock object to have all its stubbings lenient use {@link MockSettings#lenient()}. + * If you use JUnit Rules and want the entire test method to have all stubbings lenient use {@link org.mockito.junit.MockitoRule#strictness(Strictness)}. + * Refer to JUnit Jupiter Mockito extension for information how to configure strictness in JUnit5. *
  • *
  • To opt-out in Mockito 2.x, simply remove the strict stubbing setting in the test class.
  • * diff --git a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java index c8713b3776..918ac756c3 100644 --- a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java +++ b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java @@ -14,13 +14,15 @@ /** * This exception indicates presence of unused stubbings. * It is highly recommended to remove unused stubbings to keep the codebase clean. - * You can opt-out from detecting unused stubbings by configuring: + * In a rare scenario that unused stubbing is a false negative you can opt out from the validation via: *
      - *
    • JUnit rule - {@link MockitoRule#strictness(Strictness)} or {@link MockitoRule#silent()}
    • + *
    • JUnit rule - {@link MockitoRule#strictness(Strictness)} passing {@link Strictness#LENIENT}. + * Alternatively, use the old API: {@link MockitoRule#silent()}
    • *
    • JUnit runner - {@link MockitoJUnitRunner.Silent}
    • *
    • Mockito session - {@link MockitoSession}
    • + *
    • JUnit Jupiter (JUnit5) Mockito extension also has ways to configure strictness
    • *
    - * For more information about detecting unused stubbings, see {@link MockitoHint}. + * *

    * Unnecessary stubbings are stubbed method calls that were never realized during test execution. Example: *

    
    diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java
    index bebdd66a9e..77b5c6df4a 100644
    --- a/src/main/java/org/mockito/mock/MockCreationSettings.java
    +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java
    @@ -6,9 +6,11 @@
     package org.mockito.mock;
     
     import org.mockito.Incubating;
    +import org.mockito.MockSettings;
     import org.mockito.NotExtensible;
     import org.mockito.listeners.InvocationListener;
     import org.mockito.listeners.VerificationStartedListener;
    +import org.mockito.quality.Strictness;
     import org.mockito.stubbing.Answer;
     
     import java.util.List;
    @@ -110,6 +112,12 @@ public interface MockCreationSettings {
         @Incubating
         Object getOuterClassInstance();
     
    -    @Incubating//TODO x javadoc
    +    /**
    +     * Informs if the mock was created with "lenient" strictness, e.g. having {@link Strictness#LENIENT} characteristic.
    +     * For more information about using mocks with lenient strictness, see {@link MockSettings#lenient()}.
    +     *
    +     * @since TODO x
    +     */
    +    @Incubating
         boolean isLenient();
     }
    diff --git a/src/main/java/org/mockito/quality/MockitoHint.java b/src/main/java/org/mockito/quality/MockitoHint.java
    index d752159aee..f261ac1630 100644
    --- a/src/main/java/org/mockito/quality/MockitoHint.java
    +++ b/src/main/java/org/mockito/quality/MockitoHint.java
    @@ -8,10 +8,13 @@
     import org.mockito.junit.MockitoJUnitRunner;
     
     /**
    + * Stubbing hints were introduced in Mockito 2 in order to improve debuggability while keeping backwards compatibility.
    + * As Mockito 2 evolved, hints are replaced by  "strict stubbing" API ({@link Strictness}).
    + * In Mockito 3 we won't be needing hints because {@link Strictness#STRICT_STUBS} will be the default for all mocks.
    + * 

    + * Why hints? * To improve productivity when writing Java tests * stubbing hints and warnings are printed to standard output. - * See also "strict stubbing" API ({@link Strictness}) - * which drives cleaner tests and productivity more effectively than Mockito hints. *

    * Hints contain clickable links that take you right to the line of code that contains a possible problem. * Those are hints - they not necessarily indicate real problems 100% of the time. diff --git a/src/main/java/org/mockito/stubbing/Stubbing.java b/src/main/java/org/mockito/stubbing/Stubbing.java index 09e87bc444..cf9cf95d7c 100644 --- a/src/main/java/org/mockito/stubbing/Stubbing.java +++ b/src/main/java/org/mockito/stubbing/Stubbing.java @@ -6,6 +6,7 @@ import org.mockito.Incubating; import org.mockito.MockingDetails; +import org.mockito.Mockito; import org.mockito.NotExtensible; import org.mockito.invocation.Invocation; import org.mockito.quality.Strictness; @@ -53,6 +54,12 @@ public interface Stubbing extends Answer { */ boolean wasUsed(); - @Incubating//TODO x javadoc + /** + * Informs about the {@link Strictness} level of this stubbing. + * For more information about setting strictness for stubbings see {@link Mockito#lenient()}. + * + * @since TODO X + */ + @Incubating Strictness getStrictness(); } From 8f44b3be4fad8573ec9a7e8faf7f7aeba2f147b5 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 18 Jul 2018 21:14:02 +0200 Subject: [PATCH 41/50] Documented new Mockito.lenient() method --- src/main/java/org/mockito/Mockito.java | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 200065997a..3f9a0a3bff 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -4,6 +4,8 @@ */ package org.mockito; +import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.misusing.UnnecessaryStubbingException; import org.mockito.internal.InternalMockHandler; import org.mockito.internal.MockitoCore; import org.mockito.internal.creation.MockSettingsImpl; @@ -2952,7 +2954,24 @@ public static MockitoSessionBuilder mockitoSession() { return new DefaultMockitoSessionBuilder(); } - @Incubating //TODO x javadoc + /** + * Configures the stubbing to be lenient, avoiding false negatives from {@link UnnecessaryStubbingException} and {@link PotentialStubbingProblem}. + * Useful when "strict stubbing" is in use (see {@link Strictness#STRICT_STUBS} but some stubbings should remain lenient. + *

    
    +     *   lenient().when(mock.foo()).thenReturn("ok");
    +     * 
    + * Most mocks in most tests don't need any leniency and should happily prosper with {@link Strictness#STRICT_STUBS}. + * If a specific stubbing needs to be lenient, use this method. + * If a specific mock need to have stubbings lenient, use {@link MockSettings#lenient()}. + * If a specific test method / test class needs to have all stubbing lenient, + * configure strictness at the level of JUnit Rule ({@link MockitoRule}), JUnit Runner ({@link MockitoJUnitRunner}), + * Mockito Session ({@link MockitoSession}) or JUnit Jupiter extension. + *

    + * For an elaborate example when setting strictness per method/mock is useful see {@link MockSettings#lenient()}. + * + * @since TODO x + */ + @Incubating public static LenientStubber lenient() { return MOCKITO_CORE.lenient(); } From 5bebc8f697b9da68525e5eeedde09ca6a2e03ec0 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 18 Jul 2018 21:27:22 +0200 Subject: [PATCH 42/50] Updated since tags and documentation --- src/main/java/org/mockito/Mockito.java | 2 +- .../mockito/mock/MockCreationSettings.java | 2 +- .../java/org/mockito/quality/Strictness.java | 35 ++++++++++--------- .../org/mockito/stubbing/BaseStubber.java | 2 ++ .../org/mockito/stubbing/LenientStubber.java | 4 +++ .../java/org/mockito/stubbing/Stubbing.java | 2 +- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 3f9a0a3bff..a3bb7bfc55 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -2969,7 +2969,7 @@ public static MockitoSessionBuilder mockitoSession() { *

    * For an elaborate example when setting strictness per method/mock is useful see {@link MockSettings#lenient()}. * - * @since TODO x + * @since 2.20.0 */ @Incubating public static LenientStubber lenient() { diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java index 77b5c6df4a..7e74be8a90 100644 --- a/src/main/java/org/mockito/mock/MockCreationSettings.java +++ b/src/main/java/org/mockito/mock/MockCreationSettings.java @@ -116,7 +116,7 @@ public interface MockCreationSettings { * Informs if the mock was created with "lenient" strictness, e.g. having {@link Strictness#LENIENT} characteristic. * For more information about using mocks with lenient strictness, see {@link MockSettings#lenient()}. * - * @since TODO x + * @since 2.20.0 */ @Incubating boolean isLenient(); diff --git a/src/main/java/org/mockito/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index c9da8d367c..b5c6fcf8fa 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -13,27 +13,27 @@ import org.mockito.junit.MockitoRule; /** - * Configures the "strictness" of Mockito during a mocking session. - * A session typically maps to a single test method invocation. - * {@code Strictness} drives cleaner tests and better productivity. - * The easiest way to leverage enhanced {@code Strictness} is using - * Mockito's JUnit support ({@link MockitoRule} or {@link MockitoJUnitRunner}). - * If you cannot use JUnit support {@link MockitoSession} is the way to go. + * Configures the "strictness" of Mockito, affecting the behavior of stubbings and verification. + * "Strict stubbing" is a new feature in Mockito 2 that drives cleaner tests and better productivity. + * The easiest way to use it is via Mockito's JUnit support ({@link MockitoRule} or {@link MockitoJUnitRunner}). + * If you cannot use Rules or Runners try {@link MockitoSession}. *

    - * How strictness level influences the behavior of the test (mocking session)? + * How strictness influences the behavior of the test? *

      + *
    1. {@link Strictness#STRICT_STUBS} - ensures clean tests, reduces test code duplication, improves debuggability. + * Best combination of flexibility and productivity. Highly recommended. + * Planned as default for Mockito v3. + * Enable it via {@link MockitoRule}, {@link MockitoJUnitRunner} or {@link MockitoSession}. + * See {@link #STRICT_STUBS} for the details.
    2. *
    3. {@link Strictness#LENIENT} - no added behavior. * The default of Mockito 1.x. - * Recommended only if you cannot use {@link #STRICT_STUBS} nor {@link #WARN}.
    4. - *
    5. {@link Strictness#WARN} - helps keeping tests clean and improves debuggability. + * Recommended only if you cannot use {@link #STRICT_STUBS}
    6. + *
    7. {@link Strictness#WARN} - cleaner tests but only if you read the console output. * Reports console warnings about unused stubs * and stubbing argument mismatch (see {@link org.mockito.quality.MockitoHint}). - * The default behavior of Mockito 2.x when {@link JUnitRule} or {@link MockitoJUnitRunner} are used.
    8. + * The default behavior of Mockito 2.x when {@link JUnitRule} or {@link MockitoJUnitRunner} are used. * Recommended if you cannot use {@link #STRICT_STUBS}. - *
    9. {@link Strictness#STRICT_STUBS} - ensures clean tests, reduces test code duplication, improves debuggability. - * Best combination of flexibility and productivity. Highly recommended. - * Planned as default for Mockito v3. - * See {@link #STRICT_STUBS} for the details. + * Introduced originally with Mockito 2 because console warnings is the only compatible way of adding such feature.
    10. *
    * * @since 2.3.0 @@ -43,7 +43,7 @@ public enum Strictness { /** * No extra strictness. Mockito 1.x behavior. - * Recommended only if you cannot use {@link #STRICT_STUBS} nor {@link #WARN}. + * Recommended only if you cannot use {@link #STRICT_STUBS}. *

    * For more information see {@link Strictness}. * @@ -53,10 +53,10 @@ public enum Strictness { LENIENT, /** - * Helps keeping tests clean and improves debuggability. + * Helps keeping tests clean and improves debuggability only if you read the console output. * Extra warnings emitted to the console, see {@link MockitoHint}. * Default Mockito 2.x behavior. - * Recommended if you cannot use {@link #STRICT_STUBS}. + * Recommended only if you cannot use {@link #STRICT_STUBS} because console output is ignored most of the time. *

    * For more information see {@link Strictness}. * @@ -70,6 +70,7 @@ public enum Strictness { * Offers best combination of flexibility and productivity. * Highly recommended. * Planned as default for Mockito v3. + * Enable it via {@link MockitoRule}, {@link MockitoJUnitRunner} or {@link MockitoSession}. *

    * Adds following behavior: *

      diff --git a/src/main/java/org/mockito/stubbing/BaseStubber.java b/src/main/java/org/mockito/stubbing/BaseStubber.java index b9c30ef6e4..8c3192815e 100644 --- a/src/main/java/org/mockito/stubbing/BaseStubber.java +++ b/src/main/java/org/mockito/stubbing/BaseStubber.java @@ -10,6 +10,8 @@ /** * Base interface for stubbing consecutive method calls with {@link Mockito#doReturn(Object)} syntax. * This interface is needed so that we can reuse the same hierarchy in subinterfaces. + * + * @since 2.20.0 */ @NotExtensible public interface BaseStubber { diff --git a/src/main/java/org/mockito/stubbing/LenientStubber.java b/src/main/java/org/mockito/stubbing/LenientStubber.java index 4dc340c932..9088399aaa 100644 --- a/src/main/java/org/mockito/stubbing/LenientStubber.java +++ b/src/main/java/org/mockito/stubbing/LenientStubber.java @@ -10,6 +10,8 @@ /** * Used for declaring optional stubbings with {@link Mockito#lenient()} + * + * @since 2.20.0 */ @NotExtensible public interface LenientStubber extends BaseStubber { @@ -17,6 +19,8 @@ public interface LenientStubber extends BaseStubber { /** * Allows declaring the method to stub. See {@link Mockito#when(Object)}. * Needed for classic stubbing with when().then() + * + * @since 2.20.0 */ @CheckReturnValue OngoingStubbing when(T methodCall); diff --git a/src/main/java/org/mockito/stubbing/Stubbing.java b/src/main/java/org/mockito/stubbing/Stubbing.java index cf9cf95d7c..cba0917f97 100644 --- a/src/main/java/org/mockito/stubbing/Stubbing.java +++ b/src/main/java/org/mockito/stubbing/Stubbing.java @@ -58,7 +58,7 @@ public interface Stubbing extends Answer { * Informs about the {@link Strictness} level of this stubbing. * For more information about setting strictness for stubbings see {@link Mockito#lenient()}. * - * @since TODO X + * @since 2.20.0 */ @Incubating Strictness getStrictness(); From a837411b8409f3e084c82ef58c426037f04c3475 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 18 Jul 2018 21:33:32 +0200 Subject: [PATCH 43/50] Updated MockitoRule documentation --- .../java/org/mockito/junit/MockitoRule.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mockito/junit/MockitoRule.java b/src/main/java/org/mockito/junit/MockitoRule.java index 296faa7149..561fff721b 100644 --- a/src/main/java/org/mockito/junit/MockitoRule.java +++ b/src/main/java/org/mockito/junit/MockitoRule.java @@ -6,6 +6,8 @@ import org.junit.rules.MethodRule; import org.mockito.Incubating; +import org.mockito.MockSettings; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.MockitoHint; @@ -104,7 +106,7 @@ public interface MockitoRule extends MethodRule { * The strictness, especially "strict stubs" ({@link Strictness#STRICT_STUBS}) * helps debugging and keeping tests clean. * It's a new feature introduced in Mockito 2.3. - * Other levels of strictness - "Warn" - current default ({@link Strictness#WARN}) + * Other levels of strictness - "warn" - ({@link Strictness#WARN}) * and "lenient" ({@link MockitoRule#silent()}) strictness were already present in Mockito 2.1.0. * Version 2.3.0 introduces "strict stubs" ({@link Strictness#STRICT_STUBS}). * @@ -115,21 +117,12 @@ public interface MockitoRule extends MethodRule { * } *
    * - * How strictness level influences the behavior of JUnit rule: - *
      - *
    1. {@link Strictness#LENIENT} - equivalent of {@link MockitoRule#silent()} - - * no added behavior. The default of Mockito 1.x
    2. - *
    3. {@link Strictness#WARN} - helps keeping tests clean and with debuggability. - * Reports console warnings about unused stubs - * and stubbing argument mismatches (see {@link MockitoHint}). - * The default of Mockito 2.x
    4. - *
    5. {@link Strictness#STRICT_STUBS} - ensures clean tests, - * reduces test code duplication, improves debuggability. - * See the details in the Javadoc for {@link Strictness#STRICT_STUBS}. - *
    - * + * See Javadoc for {@link Strictness} to learn how strictness influences the behavior of the JUnit rule. + * See {@link Strictness#STRICT_STUBS} to learn why is it recommended to use "strict stubbing". + *

    * It is possible to tweak the strictness per test method. * Why would you need it? See the use cases in Javadoc for {@link PotentialStubbingProblem} class. + * In order to tweak strictness per stubbing see {@link Mockito#lenient()}, per mock see {@link MockSettings#lenient()}. * *

    
          * public class ExampleTest {
    @@ -146,7 +139,7 @@ public interface MockitoRule extends MethodRule {
          * }
          * 
    * - * "Strict stubs" are tentatively planned to be the default for Mockito v3 + * "Strict stubs" are planned to be the default for Mockito v3 * We are very eager to hear feedback about "strict stubbing" feature, let us know by commenting on GitHub * issue 769. * Strict stubbing is an attempt to improve testability and productivity with Mockito. Tell us what you think! From cf434ab368bbb081ccb5b8c224a12567c33728ac Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Wed, 18 Jul 2018 21:57:56 +0200 Subject: [PATCH 44/50] Documented feature in the main manual in Mockito Javadoc --- src/main/java/org/mockito/Mockito.java | 43 ++++++++++++++++++++------ 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index a3bb7bfc55..6477fa4116 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -97,12 +97,13 @@ * 37. Java 8 Custom Answer Support (Since 2.1.0)
    * 38. Meta data and generic type retention (Since 2.1.0)
    * 39. Mocking final types, enums and final methods (Since 2.1.0)
    - * 40. (*new*) Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+)
    - * 41. (**new**) Advanced public API for framework integrations (Since 2.10.+)
    - * 42. (**new**) New API for integrations: listening on verification start events (Since 2.11.+)
    - * 43. (**new**) New API for integrations: MockitoSession is usable by testing frameworks (Since 2.15.+)
    + * 40. Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+)
    + * 41. Advanced public API for framework integrations (Since 2.10.+)
    + * 42. New API for integrations: listening on verification start events (Since 2.11.+)
    + * 43. New API for integrations: MockitoSession is usable by testing frameworks (Since 2.15.+)
    * 44. Deprecated org.mockito.plugins.InstantiatorProvider as it was leaking internal API. it was replaced by org.mockito.plugins.InstantiatorProvider2 (Since 2.15.4)
    - * 45. (**new**) New JUnit Jupiter (JUnit5+) extension
    + * 45. New JUnit Jupiter (JUnit5+) extension
    + * 46. New Mockito.lenient() and MockSettings.lenient() methods (Since 2.20.0
    * * *

    0. Migrating to Mockito 2

    @@ -1369,7 +1370,7 @@ * org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker * *

    40. - * (*new*) Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+)

    + * Improved productivity and cleaner tests with "stricter" Mockito (Since 2.+) * * To quickly find out how "stricter" Mockito can make you more productive and get your tests cleaner, see: * * *

    43. - * (**new**) New API for integrations: MockitoSession is usable by testing frameworks (Since 2.15.+)

    + * New API for integrations: MockitoSession is usable by testing frameworks (Since 2.15.+) * *

    {@link MockitoSessionBuilder} and {@link MockitoSession} were enhanced to enable reuse by testing framework * integrations (e.g. {@link MockitoRule} for JUnit):

    @@ -1505,10 +1506,32 @@ * by {@link org.mockito.plugins.InstantiatorProvider2}. Old {@link org.mockito.plugins.InstantiatorProvider * instantiator providers} will continue to work, but it is recommended to switch to the new API.

    * - *

    45. (**new**) New JUnit Jupiter (JUnit5+) extension

    + *

    45. New JUnit Jupiter (JUnit5+) extension

    * * For integration with JUnit Jupiter (JUnit5+), use the `org.mockito:mockito-junit-jupiter` artifact. * For more information about the usage of the integration, see the JavaDoc of MockitoExtension. + * + *

    46. + * New Mockito.lenient() and MockSettings.lenient() methods (Since 2.20.0)

    + * + * Strict stubbing feature is available since early Mockito 2. + * It is a very useful feature because it drives cleaner tests and improved productivity. + * Strict stubbing reports unnecessary stubs, detects stubbing argument mismatch and makes the tests more DRY ({@link Strictness#STRICT_STUBS}). + * This comes with a trade-off: in some cases, you may get false negatives from strict stubbing. + * To remedy those scenarios you can now configure specific stubbing to be lenient, while all the other stubbings and mocks use strict stubbing: + * + *
    
    + *   lenient().when(mock.foo()).thenReturn("ok");
    + * 
    + * + * If you want all the stubbings on a given mock to be lenient, you can configure the mock accordingly: + * + *
    
    + *   Foo mock = Mockito.mock(Foo.class, withSettings().lenient());
    + * 
    + * + * For more information refer to {@link Mockito#lenient()} and {@link MockSettings#lenient()}. + * Let us know how do you find the new feature by opening a GitHub issue to discuss! */ @SuppressWarnings("unchecked") public class Mockito extends ArgumentMatchers { From 27876964df6738e616c28980590aff8b8e93982b Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Thu, 19 Jul 2018 00:03:38 +0200 Subject: [PATCH 45/50] Performance tweak Avoided unnecessary call to 'mockingDetails' to avoid reflective calls. Added new argument to the listener - it's ok because we're planning to make the listener public - doing so requires to move all parameters to an 'event' object. --- .../mockito/internal/handler/MockHandlerImpl.java | 9 ++++----- .../junit/DefaultStubbingLookupListener.java | 8 +++----- .../internal/listeners/StubbingLookupListener.java | 12 ++++++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 48a181ce49..5c7abd1798 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -89,7 +89,7 @@ public Object handle(Invocation invocation) throws Throwable { // look for existing answer for this invocation StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation); - notifyStubbedAnswerLookup(invocation, stubbing, mockSettings); + notifyStubbedAnswerLookup(invocation, stubbing); if (stubbing != null) { stubbing.captureArgumentsFrom(invocation); @@ -131,13 +131,12 @@ private VerificationDataImpl createVerificationData(InvocationContainerImpl invo return new VerificationDataImpl(invocationContainer, invocationMatcher); } - private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { + private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingFound) { //TODO #793 - when completed, we should be able to get rid of the casting below List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { - //TODO x pass this.invocationContainer.getStubbedInvocations() so that we don't have to call 'mockingDetails' - // each time a method is called on a mock (slow). - listener.onStubbingLookup(invocation, stubbingFound, mockSettings); + List stubbings = this.invocationContainer.getStubbedInvocations(); + listener.onStubbingLookup(invocation, stubbingFound, stubbings, this.mockSettings); } } } diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 9b0b531c14..30c549f67d 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -16,7 +16,6 @@ import java.util.LinkedList; import java.util.List; -import static org.mockito.Mockito.mockingDetails; import static org.mockito.internal.stubbing.StrictnessSelector.determineStrictness; /** @@ -32,7 +31,7 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { this.currentStrictness = strictness; } - public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings) { + public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, List allStubbings, MockCreationSettings mockSettings) { Strictness actualStrictness = determineStrictness(stubbingFound, mockSettings, currentStrictness); if (actualStrictness != Strictness.STRICT_STUBS) { @@ -42,7 +41,7 @@ public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, Mock if (stubbingFound == null) { //If stubbing was not found for invocation it means that either the mock invocation was not stubbed or //we have a stubbing arg mismatch. - List argMismatchStubbings = potentialArgMismatches(invocation); + List argMismatchStubbings = potentialArgMismatches(invocation, allStubbings); if (!argMismatchStubbings.isEmpty()) { mismatchesReported = true; Reporter.potentialStubbingProblem(invocation, argMismatchStubbings); @@ -54,9 +53,8 @@ public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, Mock } } - private static List potentialArgMismatches(Invocation invocation) { + private static List potentialArgMismatches(Invocation invocation, Collection stubbings) { List matchingStubbings = new LinkedList(); - Collection stubbings = mockingDetails(invocation.getMock()).getStubbings(); for (Stubbing s : stubbings) { if (UnusedStubbingReporting.shouldBeReported(s) && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName())) { diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java index e553168970..34a92de74c 100644 --- a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java @@ -8,6 +8,8 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Stubbing; +import java.util.List; + /** * Listens to attempts to look up stubbing answer for given mocks. This class is internal for now. *

    @@ -28,11 +30,13 @@ public interface StubbingLookupListener { /** * Called by the framework when Mockito looked up an answer for invocation on a mock. * - * TODO when making this public, we should have an event object instead of 2 arguments in the listener. - * @param invocation the invocation on the mock + * TODO when making this public, we should have an event object instead of multiple arguments in the listener. + * + * @param invocation - the invocation that causes stubbing lookup * @param stubbingFound - can be null - it indicates that the invocation was not stubbed. - * @param mockSettings + * @param allStubbings - all stubbings declared on the mock object that we are invoking. + * @param mockSettings - settings of the mock object that we are invoking */ - void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, MockCreationSettings mockSettings); + void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, List allStubbings, MockCreationSettings mockSettings); } From bef4d861da694e39ed4040152bcd94d184476a41 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Fri, 20 Jul 2018 18:09:38 -0700 Subject: [PATCH 46/50] Fixed problem with the stubbings order --- .../mockito/internal/handler/MockHandlerImpl.java | 3 ++- .../junit/DefaultStubbingLookupListener.java | 2 +- .../internal/listeners/StubbingLookupListener.java | 4 ++-- .../internal/stubbing/InvocationContainerImpl.java | 14 ++++++++++++++ .../internal/util/DefaultMockingDetails.java | 8 +------- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java index 5c7abd1798..bd50535ade 100644 --- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java +++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java @@ -21,6 +21,7 @@ import org.mockito.stubbing.Stubbing; import org.mockito.verification.VerificationMode; +import java.util.Collection; import java.util.List; import static org.mockito.internal.exceptions.Reporter.stubPassedToVerify; @@ -135,7 +136,7 @@ private void notifyStubbedAnswerLookup(Invocation invocation, Stubbing stubbingF //TODO #793 - when completed, we should be able to get rid of the casting below List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners(); for (StubbingLookupListener listener : listeners) { - List stubbings = this.invocationContainer.getStubbedInvocations(); + Collection stubbings = this.invocationContainer.getStubbingsAscending(); listener.onStubbingLookup(invocation, stubbingFound, stubbings, this.mockSettings); } } diff --git a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java index 30c549f67d..5c99068ef7 100644 --- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java @@ -31,7 +31,7 @@ class DefaultStubbingLookupListener implements StubbingLookupListener { this.currentStrictness = strictness; } - public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, List allStubbings, MockCreationSettings mockSettings) { + public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, Collection allStubbings, MockCreationSettings mockSettings) { Strictness actualStrictness = determineStrictness(stubbingFound, mockSettings, currentStrictness); if (actualStrictness != Strictness.STRICT_STUBS) { diff --git a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java index 34a92de74c..fa7daf3914 100644 --- a/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java +++ b/src/main/java/org/mockito/internal/listeners/StubbingLookupListener.java @@ -8,7 +8,7 @@ import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Stubbing; -import java.util.List; +import java.util.Collection; /** * Listens to attempts to look up stubbing answer for given mocks. This class is internal for now. @@ -37,6 +37,6 @@ public interface StubbingLookupListener { * @param allStubbings - all stubbings declared on the mock object that we are invoking. * @param mockSettings - settings of the mock object that we are invoking */ - void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, List allStubbings, MockCreationSettings mockSettings); + void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, Collection allStubbings, MockCreationSettings mockSettings); } diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index d34a70b5e5..3e77ef3723 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -18,6 +18,8 @@ import org.mockito.stubbing.ValidableAnswer; import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -135,10 +137,22 @@ public void clearInvocations() { registeredInvocations.clear(); } + /** + * Stubbings in descending order, most recent first + */ public List getStubbedInvocations() { return (List) stubbed; } + /** + * Stubbings in ascending order, most recent last + */ + public Collection getStubbingsAscending() { + List result = new LinkedList(stubbed); + Collections.reverse(result); + return result; + } + public Object invokedMock() { return invocationForStubbing.getInvocation().getMock(); } diff --git a/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java b/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java index 0c3669c9dc..4f739cdbc7 100644 --- a/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java +++ b/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java @@ -8,15 +8,12 @@ import org.mockito.exceptions.misusing.NotAMockException; import org.mockito.internal.debugging.InvocationsPrinter; import org.mockito.internal.stubbing.InvocationContainerImpl; -import org.mockito.internal.stubbing.StubbingComparator; import org.mockito.invocation.Invocation; import org.mockito.invocation.MockHandler; import org.mockito.mock.MockCreationSettings; import org.mockito.stubbing.Stubbing; import java.util.Collection; -import java.util.List; -import java.util.TreeSet; /** * Class to inspect any object, and identify whether a particular object is either a mock or a spy. This is @@ -57,10 +54,7 @@ public MockCreationSettings getMockCreationSettings() { @Override public Collection getStubbings() { - List stubbings = getInvocationContainer().getStubbedInvocations(); - TreeSet out = new TreeSet(new StubbingComparator()); - out.addAll(stubbings); - return out; + return getInvocationContainer().getStubbingsAscending(); } @Override From f30ec35385ea24fbe35517b79dc3ec47ebc89b7c Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 21 Jul 2018 05:23:12 -0700 Subject: [PATCH 47/50] Rename job for consistency --- .../java/org/mockito/internal/invocation/UnusedStubsFinder.java | 2 +- .../org/mockito/internal/stubbing/InvocationContainerImpl.java | 2 +- .../internal/stubbing/defaultanswers/ReturnsDeepStubs.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mockito/internal/invocation/UnusedStubsFinder.java b/src/main/java/org/mockito/internal/invocation/UnusedStubsFinder.java index 0f9ac6dc0f..5aec36dc13 100644 --- a/src/main/java/org/mockito/internal/invocation/UnusedStubsFinder.java +++ b/src/main/java/org/mockito/internal/invocation/UnusedStubsFinder.java @@ -23,7 +23,7 @@ public class UnusedStubsFinder { public List find(List mocks) { List unused = new LinkedList(); for (Object mock : mocks) { - List fromSingleMock = MockUtil.getInvocationContainer(mock).getStubbedInvocations(); + List fromSingleMock = MockUtil.getInvocationContainer(mock).getStubbingsDescending(); for(Stubbing s : fromSingleMock) { if (!s.wasUsed()) { unused.add(s.getInvocation()); diff --git a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java index 3e77ef3723..6c98f37c9f 100644 --- a/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java +++ b/src/main/java/org/mockito/internal/stubbing/InvocationContainerImpl.java @@ -140,7 +140,7 @@ public void clearInvocations() { /** * Stubbings in descending order, most recent first */ - public List getStubbedInvocations() { + public List getStubbingsDescending() { return (List) stubbed; } 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 901c0a6810..3909ff041c 100644 --- a/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java +++ b/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsDeepStubs.java @@ -61,7 +61,7 @@ private Object deepStub(InvocationOnMock invocation, GenericMetadataSupport retu // matches invocation for verification // TODO why don't we do container.findAnswer here? - for (Stubbing stubbing : container.getStubbedInvocations()) { + for (Stubbing stubbing : container.getStubbingsDescending()) { if (container.getInvocationForStubbing().matches(stubbing.getInvocation())) { return stubbing.answer(invocation); } From cf62c73422a14641ddcaad9c4a8f581daf899049 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 21 Jul 2018 05:25:08 -0700 Subject: [PATCH 48/50] Finalized the documentation --- src/main/java/org/mockito/MockSettings.java | 58 ++------------- src/main/java/org/mockito/Mockito.java | 72 +++++++++++++++---- .../misusing/PotentialStubbingProblem.java | 9 +-- .../UnnecessaryStubbingException.java | 30 ++++---- .../java/org/mockito/quality/Strictness.java | 11 +-- 5 files changed, 90 insertions(+), 90 deletions(-) diff --git a/src/main/java/org/mockito/MockSettings.java b/src/main/java/org/mockito/MockSettings.java index a31ea8ecbe..c79c243175 100644 --- a/src/main/java/org/mockito/MockSettings.java +++ b/src/main/java/org/mockito/MockSettings.java @@ -5,6 +5,7 @@ package org.mockito; import org.mockito.exceptions.misusing.PotentialStubbingProblem; +import org.mockito.exceptions.misusing.UnnecessaryStubbingException; import org.mockito.invocation.InvocationFactory; import org.mockito.invocation.MockHandler; import org.mockito.listeners.InvocationListener; @@ -320,62 +321,15 @@ public interface MockSettings extends Serializable { MockCreationSettings build(Class typeToMock); /** - * Mock will have {@link Strictness#LENIENT} strictness. - * Useful when all mocks of the test are stricter but you want a specific mock to be lenient. + * Lenient mocks bypass "strict stubbing" validation (see {@link Strictness#STRICT_STUBS}). + * When mock is declared as lenient none of its stubbings will be checked for potential stubbing problems such as + * 'unnecessary stubbing' ({@link UnnecessaryStubbingException}) or for 'stubbing argument mismatch' {@link PotentialStubbingProblem}. + * *

    
          *   Foo mock = mock(Foo.class, withSettings.lenient());
          * 
    - *

    - * Most mocks in most tests should use strict stubbing ({@link Strictness#STRICT_STUBS} because it gives the best developer experience. - * There are rare scenarios where strict stubbing gives false negative signal: - *

      - *
    • detecting stubbing argument mismatch - see {@link PotentialStubbingProblem}
    • - *
    • detecting unused stubbings - see {@link PotentialStubbingProblem})
    • - *
    - * If you false negatives in any of those scenarios you can configure a specific mock to be lenient. - * If there is only one (few) stubbings that need to be lenient it is better to set the stubbing to be lenient, rather than the entire mock object. - * See {@link Mockito#lenient()}. - *

    - * In below example, 'foo.foo()' is a default stubbing that was moved to 'before()' method to avoid duplication. - * Doing so makes the 'test3()' method fail with unnecessary stubbing on 'foo.foo()'. - * To resolve it we can configure 'Foo' mock to be lenient or configure 'foo.foo()' stubbing to be lenient. - *

    - * Note that this example is contrived. - * It is not desired to eliminate all possible duplication from the test code - * because it may add complexity and conceal important test information. - * Some repetition in tests is OK, use your own judgement to write great tests! - * - *

    
    -     * public class SomeTest {
    -     *
    -     *     @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(STRICT_STUBS);
          *
    -     *     @Mock Foo foo;
    -     *     @Mock Bar bar;
    -     *
    -     *     @Before public void before() {
    -     *         when(foo.foo()).thenReturn("ok");
    -     *
    -     *         // it is better to configure the stubbing to be lenient:
    -     *         // lenient().when(foo.foo()).thenReturn("ok");
    -     *
    -     *         // or the entire mock to be lenient:
    -     *         // foo = mock(Foo.class, withSettings().lenient());
    -     *     }
    -     *
    -     *     @Test public void test1() {
    -     *         foo.foo();
    -     *     }
    -     *
    -     *     @Test public void test2() {
    -     *         foo.foo();
    -     *     }
    -     *
    -     *     @Test public void test3() {
    -     *         bar.bar();
    -     *     }
    -     * }
    -     * 
    + * For more information and an elaborate example, see {@link Mockito#lenient()}. */ @Incubating MockSettings lenient(); diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 6477fa4116..655fe3d557 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -1515,7 +1515,7 @@ * New Mockito.lenient() and MockSettings.lenient() methods (Since 2.20.0) * * Strict stubbing feature is available since early Mockito 2. - * It is a very useful feature because it drives cleaner tests and improved productivity. + * It is very useful because it drives cleaner tests and improved productivity. * Strict stubbing reports unnecessary stubs, detects stubbing argument mismatch and makes the tests more DRY ({@link Strictness#STRICT_STUBS}). * This comes with a trade-off: in some cases, you may get false negatives from strict stubbing. * To remedy those scenarios you can now configure specific stubbing to be lenient, while all the other stubbings and mocks use strict stubbing: @@ -1530,7 +1530,7 @@ * Foo mock = Mockito.mock(Foo.class, withSettings().lenient()); * * - * For more information refer to {@link Mockito#lenient()} and {@link MockSettings#lenient()}. + * For more information refer to {@link Mockito#lenient()}. * Let us know how do you find the new feature by opening a GitHub issue to discuss! */ @SuppressWarnings("unchecked") @@ -2978,19 +2978,67 @@ public static MockitoSessionBuilder mockitoSession() { } /** - * Configures the stubbing to be lenient, avoiding false negatives from {@link UnnecessaryStubbingException} and {@link PotentialStubbingProblem}. - * Useful when "strict stubbing" is in use (see {@link Strictness#STRICT_STUBS} but some stubbings should remain lenient. + * Lenient stubs bypass "strict stubbing" validation (see {@link Strictness#STRICT_STUBS}). + * When stubbing is declared as lenient, it will not be checked for potential stubbing problems such as + * 'unnecessary stubbing' ({@link UnnecessaryStubbingException}) or for 'stubbing argument mismatch' {@link PotentialStubbingProblem}. + * *
    
          *   lenient().when(mock.foo()).thenReturn("ok");
          * 
    - * Most mocks in most tests don't need any leniency and should happily prosper with {@link Strictness#STRICT_STUBS}. - * If a specific stubbing needs to be lenient, use this method. - * If a specific mock need to have stubbings lenient, use {@link MockSettings#lenient()}. - * If a specific test method / test class needs to have all stubbing lenient, - * configure strictness at the level of JUnit Rule ({@link MockitoRule}), JUnit Runner ({@link MockitoJUnitRunner}), - * Mockito Session ({@link MockitoSession}) or JUnit Jupiter extension. - *

    - * For an elaborate example when setting strictness per method/mock is useful see {@link MockSettings#lenient()}. + * + * Most mocks in most tests don't need leniency and should happily prosper with {@link Strictness#STRICT_STUBS}. + *

      + *
    • If a specific stubbing needs to be lenient - use this method
    • + *
    • If a specific mock need to have stubbings lenient - use {@link MockSettings#lenient()}
    • + *
    • If a specific test method / test class needs to have all stubbings lenient + * - configure strictness at the level of JUnit Rule ({@link MockitoRule}), JUnit Runner ({@link MockitoJUnitRunner}), + * Mockito Session ({@link MockitoSession}) or JUnit Jupiter extension
    • + *
    + * + *

    Elaborate example

    + * + * In below example, 'foo.foo()' is a stubbing that was moved to 'before()' method to avoid duplication. + * Doing so makes one of the test methods ('test3()') fail with 'unnecessary stubbing'. + * To resolve it we can configure 'foo.foo()' stubbing in 'before()' method to be lenient. + * Alternatively, we can configure entire 'foo' mock as lenient. + *

    + * This example is simplified and not realistic. + * Pushing stubbings to 'before()' method may cause tests to be less readable. + * Some repetition in tests is OK, use your own judgement to write great tests! + * It is not desired to eliminate all possible duplication from the test code + * because it may add complexity and conceal important test information. + * + *

    
    +     * public class SomeTest {
    +     *
    +     *     @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(STRICT_STUBS);
    +     *
    +     *     @Mock Foo foo;
    +     *     @Mock Bar bar;
    +     *
    +     *     @Before public void before() {
    +     *         when(foo.foo()).thenReturn("ok");
    +     *
    +     *         // it is better to configure the stubbing to be lenient:
    +     *         // lenient().when(foo.foo()).thenReturn("ok");
    +     *
    +     *         // or the entire mock to be lenient:
    +     *         // foo = mock(Foo.class, withSettings().lenient());
    +     *     }
    +     *
    +     *     @Test public void test1() {
    +     *         foo.foo();
    +     *     }
    +     *
    +     *     @Test public void test2() {
    +     *         foo.foo();
    +     *     }
    +     *
    +     *     @Test public void test3() {
    +     *         bar.bar();
    +     *     }
    +     * }
    +     * 
    * * @since 2.20.0 */ diff --git a/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java b/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java index 82ca9ed28c..5eb6a77ae8 100644 --- a/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java +++ b/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java @@ -4,10 +4,9 @@ */ package org.mockito.exceptions.misusing; -import org.mockito.MockSettings; import org.mockito.Mockito; -import org.mockito.quality.Strictness; import org.mockito.exceptions.base.MockitoException; +import org.mockito.quality.Strictness; /** * {@code PotentialStubbingProblem} improves productivity by failing the test early when the user @@ -54,11 +53,7 @@ * It is a well known limitation of Mockito API and another example how Mockito optimizes its clean API for 95% of the cases * while still supporting edge cases. * - *
  • Reduce the strictness level. If you need one (few) stubbings to be lenient use {@link Mockito#lenient()}. - * If you want a single mock object to have all its stubbings lenient use {@link MockSettings#lenient()}. - * If you use JUnit Rules and want the entire test method to have all stubbings lenient use {@link org.mockito.junit.MockitoRule#strictness(Strictness)}. - * Refer to JUnit Jupiter Mockito extension for information how to configure strictness in JUnit5. - *
  • + *
  • Reduce the strictness level per stubbing, per mock or per test - see {@link Mockito#lenient()}
  • *
  • To opt-out in Mockito 2.x, simply remove the strict stubbing setting in the test class.
  • * *

    diff --git a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java index 918ac756c3..9c3dca170b 100644 --- a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java +++ b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java @@ -4,24 +4,31 @@ */ package org.mockito.exceptions.misusing; +import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.exceptions.base.MockitoException; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoRule; -import org.mockito.quality.MockitoHint; import org.mockito.quality.Strictness; /** * This exception indicates presence of unused stubbings. * It is highly recommended to remove unused stubbings to keep the codebase clean. - * In a rare scenario that unused stubbing is a false negative you can opt out from the validation via: - *

      - *
    • JUnit rule - {@link MockitoRule#strictness(Strictness)} passing {@link Strictness#LENIENT}. - * Alternatively, use the old API: {@link MockitoRule#silent()}
    • - *
    • JUnit runner - {@link MockitoJUnitRunner.Silent}
    • - *
    • Mockito session - {@link MockitoSession}
    • - *
    • JUnit Jupiter (JUnit5) Mockito extension also has ways to configure strictness
    • - *
    + * In a rare scenario that unused stubbing is a false negative you can opt out from the validation via + * (in order of ascending scope): + *
      + *
    1. Declaring specific stubbing as 'lenient' - {@link Mockito#lenient()}
    2. + *
    3. Declaring specific mock as 'lenient' - {@link org.mockito.MockSettings#lenient()}
    4. + *
    5. Declaring all mocks in given test class or test method mock as 'lenient' with: + *
        + *
      • JUnit rule - {@link MockitoRule#strictness(Strictness)} passing {@link Strictness#LENIENT}. + * Alternatively, use the old API: {@link MockitoRule#silent()}
      • + *
      • JUnit runner - {@link MockitoJUnitRunner.Silent}
      • + *
      • Mockito session - {@link MockitoSession}
      • + *
      • JUnit Jupiter (JUnit5) Mockito extension also has ways to configure strictness
      • + *
      + *
    6. + *
    * *

    * Unnecessary stubbings are stubbed method calls that were never realized during test execution. Example: @@ -47,11 +54,6 @@ * Mockito JUnit Runner triggers UnnecessaryStubbingException only when none of the test methods use the stubbings. * This means that it is ok to put default stubbing in a 'setup' method or in test class constructor. * That default stubbing needs to be used at least once by one of the test methods. - *

    - * To find out more about detecting unused stubbings see {@link MockitoHint}. - * See javadoc for {@link MockitoRule} to understand the behavior or Mockito JUnit Rules. - * See javadoc for {@link MockitoJUnitRunner} to find out how Mockito JUnit Runner detects unused stubs. - * See javadoc for {@link MockitoSession} to find out about detecting unused stubs without JUnit. */ public class UnnecessaryStubbingException extends MockitoException { public UnnecessaryStubbingException(String message) { diff --git a/src/main/java/org/mockito/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index b5c6fcf8fa..f633a555aa 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -15,7 +15,8 @@ /** * Configures the "strictness" of Mockito, affecting the behavior of stubbings and verification. * "Strict stubbing" is a new feature in Mockito 2 that drives cleaner tests and better productivity. - * The easiest way to use it is via Mockito's JUnit support ({@link MockitoRule} or {@link MockitoJUnitRunner}). + * The easiest way to use it is via Mockito's JUnit support ({@link MockitoRule}, {@link MockitoJUnitRunner}) + * or our JUnit Jupiter (JUnit5) extension. * If you cannot use Rules or Runners try {@link MockitoSession}. *

    * How strictness influences the behavior of the test? @@ -33,7 +34,7 @@ * and stubbing argument mismatch (see {@link org.mockito.quality.MockitoHint}). * The default behavior of Mockito 2.x when {@link JUnitRule} or {@link MockitoJUnitRunner} are used. * Recommended if you cannot use {@link #STRICT_STUBS}. - * Introduced originally with Mockito 2 because console warnings is the only compatible way of adding such feature. + * Introduced originally with Mockito 2 because console warnings was the only compatible way of adding such feature. * * * @since 2.3.0 @@ -70,7 +71,8 @@ public enum Strictness { * Offers best combination of flexibility and productivity. * Highly recommended. * Planned as default for Mockito v3. - * Enable it via {@link MockitoRule}, {@link MockitoJUnitRunner} or {@link MockitoSession}. + * Enable it via {@link MockitoRule}, {@link MockitoJUnitRunner}, JUnit Jupiter (JUnit5) extension + * or {@link MockitoSession}. *

    * Adds following behavior: *

      @@ -81,8 +83,7 @@ public enum Strictness { *
    • Cleaner, more DRY tests ("Don't Repeat Yourself"): * If you use {@link org.mockito.Mockito#verifyNoMoreInteractions(Object...)} * you no longer need to explicitly verify stubbed invocations. - * They are automatically verified for you. However if you have more invocations, - * the test won't fail since it won't check that there are no more interactions on that stub.
    • + * They are automatically verified for you. *
    * * For more information see {@link Strictness}. From e51245a7cec47f9d0a548a0f580404a499f23761 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 21 Jul 2018 05:29:08 -0700 Subject: [PATCH 49/50] Bumped version --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index dbf9e8dfb8..6a56cf9978 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ #Currently building Mockito version -version=2.19.7 +version=2.20.0 #Previous version used to generate release notes delta previousVersion=2.19.6 From 0fcc2a309372056e15646c9792baa733133df804 Mon Sep 17 00:00:00 2001 From: Szczepan Faber Date: Sat, 21 Jul 2018 05:37:14 -0700 Subject: [PATCH 50/50] Made documentation consistent. --- src/main/java/org/mockito/Mockito.java | 4 +--- .../misusing/UnnecessaryStubbingException.java | 14 ++------------ src/main/java/org/mockito/junit/MockitoJUnit.java | 9 +++++++-- src/main/java/org/mockito/quality/Strictness.java | 8 +++----- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/mockito/Mockito.java b/src/main/java/org/mockito/Mockito.java index 655fe3d557..fb03b91467 100644 --- a/src/main/java/org/mockito/Mockito.java +++ b/src/main/java/org/mockito/Mockito.java @@ -2991,9 +2991,7 @@ public static MockitoSessionBuilder mockitoSession() { *
  • If a specific stubbing needs to be lenient - use this method
  • *
  • If a specific mock need to have stubbings lenient - use {@link MockSettings#lenient()}
  • *
  • If a specific test method / test class needs to have all stubbings lenient - * - configure strictness at the level of JUnit Rule ({@link MockitoRule}), JUnit Runner ({@link MockitoJUnitRunner}), - * Mockito Session ({@link MockitoSession}) or JUnit Jupiter extension
  • - * + * - configure strictness using our JUnit support ({@link MockitoJUnit} or Mockito Session ({@link MockitoSession}) * *

    Elaborate example

    * diff --git a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java index 9c3dca170b..2d4605b4f6 100644 --- a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java +++ b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java @@ -7,9 +7,6 @@ import org.mockito.Mockito; import org.mockito.MockitoSession; import org.mockito.exceptions.base.MockitoException; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; -import org.mockito.quality.Strictness; /** * This exception indicates presence of unused stubbings. @@ -19,15 +16,8 @@ *
      *
    1. Declaring specific stubbing as 'lenient' - {@link Mockito#lenient()}
    2. *
    3. Declaring specific mock as 'lenient' - {@link org.mockito.MockSettings#lenient()}
    4. - *
    5. Declaring all mocks in given test class or test method mock as 'lenient' with: - *
        - *
      • JUnit rule - {@link MockitoRule#strictness(Strictness)} passing {@link Strictness#LENIENT}. - * Alternatively, use the old API: {@link MockitoRule#silent()}
      • - *
      • JUnit runner - {@link MockitoJUnitRunner.Silent}
      • - *
      • Mockito session - {@link MockitoSession}
      • - *
      • JUnit Jupiter (JUnit5) Mockito extension also has ways to configure strictness
      • - *
      - *
    6. + *
    7. Declaring all mocks in given test class or test method mock as 'lenient' with + * our JUnit support ({@link org.mockito.junit.MockitoJUnit}) or Mockito session ({@link MockitoSession})
    8. *
    * *

    diff --git a/src/main/java/org/mockito/junit/MockitoJUnit.java b/src/main/java/org/mockito/junit/MockitoJUnit.java index 2f8b65f3bf..da9428ba41 100644 --- a/src/main/java/org/mockito/junit/MockitoJUnit.java +++ b/src/main/java/org/mockito/junit/MockitoJUnit.java @@ -5,13 +5,18 @@ package org.mockito.junit; import org.mockito.Incubating; -import org.mockito.quality.Strictness; import org.mockito.internal.junit.JUnitRule; import org.mockito.internal.junit.VerificationCollectorImpl; import org.mockito.internal.util.ConsoleMockitoLogger; +import org.mockito.quality.Strictness; /** - * The JUnit rule can be used instead of {@link MockitoJUnitRunner}. See {@link MockitoRule}. + * Mockito supports JUnit via: + *

  • + *
      JUnit Rules - see {@link MockitoRule}
    + *
      JUnit runners - see {@link MockitoJUnitRunner}
    + * + *
  • * * @since 1.10.17 */ diff --git a/src/main/java/org/mockito/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index f633a555aa..ea9fed82d6 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -9,15 +9,14 @@ import org.mockito.exceptions.misusing.PotentialStubbingProblem; import org.mockito.exceptions.misusing.UnnecessaryStubbingException; import org.mockito.internal.junit.JUnitRule; +import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoRule; /** * Configures the "strictness" of Mockito, affecting the behavior of stubbings and verification. * "Strict stubbing" is a new feature in Mockito 2 that drives cleaner tests and better productivity. - * The easiest way to use it is via Mockito's JUnit support ({@link MockitoRule}, {@link MockitoJUnitRunner}) - * or our JUnit Jupiter (JUnit5) extension. - * If you cannot use Rules or Runners try {@link MockitoSession}. + * The easiest way to leverage it is via Mockito's JUnit support ({@link MockitoJUnit}) or Mockito Session ({@link MockitoSession}). *

    * How strictness influences the behavior of the test? *

      @@ -71,8 +70,7 @@ public enum Strictness { * Offers best combination of flexibility and productivity. * Highly recommended. * Planned as default for Mockito v3. - * Enable it via {@link MockitoRule}, {@link MockitoJUnitRunner}, JUnit Jupiter (JUnit5) extension - * or {@link MockitoSession}. + * Enable it via our JUnit support ({@link MockitoJUnit}) or {@link MockitoSession}. *

      * Adds following behavior: *