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/MockSettings.java b/src/main/java/org/mockito/MockSettings.java
index 82dc0500a5..c79c243175 100644
--- a/src/main/java/org/mockito/MockSettings.java
+++ b/src/main/java/org/mockito/MockSettings.java
@@ -4,12 +4,15 @@
*/
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;
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 +319,18 @@ public interface MockSettings extends Serializable {
*/
@Incubating
{@link MockitoSessionBuilder} and {@link MockitoSession} were enhanced to enable reuse by testing framework
* integrations (e.g. {@link MockitoRule} for JUnit):
+ *
+ * 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 2a384885e2..fb03b91467 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;
@@ -28,6 +30,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;
@@ -94,12 +97,13 @@
* 37. Java 8 Custom Answer Support (Since 2.1.0)
+ * Foo mock = mock(Foo.class, withSettings.lenient());
+ *
* 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
@@ -1366,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:
*
@@ -1394,7 +1398,7 @@
* issue 769.
*
*
*
* 41.
- * (**new**) Advanced public API for framework integrations (Since 2.10.+)
+ * Advanced public API for framework integrations (Since 2.10.+)
*
* In Summer 2017 we decided that Mockito
*
@@ -1447,7 +1451,7 @@
* Do you have feedback? Please leave comment in issue 1110.
*
* 42.
- * (**new**) New API for integrations: listening on verification start events (Since 2.11.+)
+ * New API for integrations: listening on verification start events (Since 2.11.+)
*
* Framework integrations such as Spring Boot needs public API to tackle double-proxy use case
* (issue 1191).
@@ -1467,7 +1471,7 @@
* 43.
- * (**new**) New API for integrations:
+ * New API for integrations: MockitoSession
is usable by testing frameworks (Since 2.15.+)MockitoSession
is usable by testing frameworks (Since 2.15.+)
*
*
MockitoExtension
.
+ *
+ * Mockito.lenient()
and MockSettings.lenient()
methods (Since 2.20.0)
+ * 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()}.
+ * Let us know how do you find the new feature by opening a GitHub issue to discuss!
*/
@SuppressWarnings("unchecked")
public class Mockito extends ArgumentMatchers {
@@ -2950,4 +2976,72 @@ public static MockitoFramework framework() {
public static MockitoSessionBuilder mockitoSession() {
return new DefaultMockitoSessionBuilder();
}
+
+ /**
+ * 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 leniency and should happily prosper with {@link Strictness#STRICT_STUBS}.
+ * + * 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
+ */
+ @Incubating
+ public static LenientStubber lenient() {
+ return MOCKITO_CORE.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..5eb6a77ae8 100644
--- a/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java
+++ b/src/main/java/org/mockito/exceptions/misusing/PotentialStubbingProblem.java
@@ -5,8 +5,8 @@
package org.mockito.exceptions.misusing;
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
@@ -53,23 +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.
*
- *
- * 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.
- * diff --git a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java index c8713b3776..2d4605b4f6 100644 --- a/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java +++ b/src/main/java/org/mockito/exceptions/misusing/UnnecessaryStubbingException.java @@ -4,23 +4,22 @@ */ 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. - * You can opt-out from detecting unused stubbings by configuring: - *
* Unnecessary stubbings are stubbed method calls that were never realized during test execution. Example: *
@@ -45,11 +44,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/internal/MockitoCore.java b/src/main/java/org/mockito/internal/MockitoCore.java
index 419d228436..c764e59825 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,8 @@
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;
import org.mockito.verification.VerificationMode;
@@ -165,10 +168,14 @@ public InOrder inOrder(Object... mocks) {
}
public Stubber stubber() {
+ return stubber(null);
+ }
+
+ public Stubber stubber(Strictness strictness) {
MockingProgress mockingProgress = mockingProgress();
mockingProgress.stubbingStarted();
mockingProgress.resetOngoingStubbing();
- return new StubberImpl();
+ return new StubberImpl(strictness);
}
public void validateMockitoUsage() {
@@ -202,4 +209,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/creation/MockSettingsImpl.java b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
index 82af98aeb8..ca67730793 100644
--- a/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
+++ b/src/main/java/org/mockito/internal/creation/MockSettingsImpl.java
@@ -226,6 +226,12 @@ public MockCreationSettings build(Class typeToMock) {
return validatedSettings(typeToMock, (CreationSettings) this);
}
+ @Override
+ public MockSettings lenient() {
+ this.lenient = true;
+ 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..03afd80947 100644
--- a/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
+++ b/src/main/java/org/mockito/internal/creation/settings/CreationSettings.java
@@ -37,6 +37,7 @@ public class CreationSettings implements MockCreationSettings, Serializabl
private boolean useConstructor;
private Object outerClassInstance;
private Object[] constructorArgs;
+ protected boolean lenient;
public CreationSettings() {}
@@ -55,6 +56,7 @@ public CreationSettings(CreationSettings copy) {
this.useConstructor = copy.isUsingConstructor();
this.outerClassInstance = copy.getOuterClassInstance();
this.constructorArgs = copy.getConstructorArgs();
+ this.lenient = copy.lenient;
}
@Override
@@ -153,4 +155,9 @@ public Object getOuterClassInstance() {
public boolean isStubOnly() {
return stubOnly;
}
+
+ @Override
+ public boolean isLenient() {
+ return lenient;
+ }
}
diff --git a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java
index 57a9d435b0..bd50535ade 100644
--- a/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java
+++ b/src/main/java/org/mockito/internal/handler/MockHandlerImpl.java
@@ -18,8 +18,10 @@
import org.mockito.invocation.InvocationContainer;
import org.mockito.invocation.MockHandler;
import org.mockito.mock.MockCreationSettings;
+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;
@@ -130,11 +132,12 @@ private VerificationDataImpl createVerificationData(InvocationContainerImpl invo
return new VerificationDataImpl(invocationContainer, invocationMatcher);
}
- private void notifyStubbedAnswerLookup(Invocation invocation, StubbedInvocationMatcher exception) {
+ 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) mockSettings).getStubbingLookupListeners();
+ List listeners = ((CreationSettings) this.mockSettings).getStubbingLookupListeners();
for (StubbingLookupListener listener : listeners) {
- listener.onStubbingLookup(invocation, exception);
+ Collection stubbings = this.invocationContainer.getStubbingsAscending();
+ listener.onStubbingLookup(invocation, stubbingFound, stubbings, this.mockSettings);
}
}
}
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/junit/DefaultStubbingLookupListener.java b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
index c7e2551e90..5c99068ef7 100644
--- a/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
+++ b/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java
@@ -6,8 +6,9 @@
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.invocation.MatchableInvocation;
+import org.mockito.mock.MockCreationSettings;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Stubbing;
@@ -15,7 +16,7 @@
import java.util.LinkedList;
import java.util.List;
-import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.internal.stubbing.StrictnessSelector.determineStrictness;
/**
* Default implementation of stubbing lookup listener.
@@ -30,15 +31,17 @@ class DefaultStubbingLookupListener implements StubbingLookupListener {
this.currentStrictness = strictness;
}
- public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbingFound) {
- if (currentStrictness != Strictness.STRICT_STUBS) {
+ public void onStubbingLookup(Invocation invocation, Stubbing stubbingFound, Collection allStubbings, MockCreationSettings mockSettings) {
+ Strictness actualStrictness = determineStrictness(stubbingFound, mockSettings, currentStrictness);
+
+ if (actualStrictness != Strictness.STRICT_STUBS) {
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.
- List argMismatchStubbings = potentialArgMismatches(invocation);
+ List argMismatchStubbings = potentialArgMismatches(invocation, allStubbings);
if (!argMismatchStubbings.isEmpty()) {
mismatchesReported = true;
Reporter.potentialStubbingProblem(invocation, argMismatchStubbings);
@@ -50,11 +53,11 @@ public void onStubbingLookup(Invocation invocation, MatchableInvocation stubbing
}
}
- 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 (!s.wasUsed() && s.getInvocation().getMethod().getName().equals(invocation.getMethod().getName())) {
+ 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/UnusedStubbings.java b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java
index 5b6434d840..3505d2cb1c 100644
--- a/src/main/java/org/mockito/internal/junit/UnusedStubbings.java
+++ b/src/main/java/org/mockito/internal/junit/UnusedStubbings.java
@@ -6,11 +6,11 @@
import org.mockito.internal.exceptions.Reporter;
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;
/**
@@ -47,16 +47,19 @@ public String toString() {
return unused.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();
- }
- });
-
+ void reportUnused() {
+ if (unused.isEmpty()) {
+ return;
+ }
- Reporter.unncessaryStubbingException(invocations);
+ List invocations = new LinkedList();
+ for (Stubbing stubbing : unused) {
+ invocations.add(stubbing.getInvocation());
}
+ if (invocations.isEmpty()) {
+ return;
+ }
+
+ Reporter.unncessaryStubbingException(invocations);
}
}
diff --git a/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java b/src/main/java/org/mockito/internal/junit/UnusedStubbingsFinder.java
index 715c39326a..14c61c5578 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.stubbing.UnusedStubbingReporting;
import org.mockito.internal.util.collections.ListUtil.Filter;
import org.mockito.invocation.Invocation;
+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,15 @@
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
*
- * How strictness level influences the behavior of JUnit rule:
- * * 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!
diff --git a/src/main/java/org/mockito/mock/MockCreationSettings.java b/src/main/java/org/mockito/mock/MockCreationSettings.java
index b58daf0efa..7e74be8a90 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;
@@ -109,4 +111,13 @@ public interface MockCreationSettings+ * 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/quality/Strictness.java b/src/main/java/org/mockito/quality/Strictness.java index c9da8d367c..ea9fed82d6 100644 --- a/src/main/java/org/mockito/quality/Strictness.java +++ b/src/main/java/org/mockito/quality/Strictness.java @@ -9,31 +9,31 @@ 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 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 leverage it is via Mockito's JUnit support ({@link MockitoJUnit}) or Mockito Session ({@link MockitoSession}). *
- * How strictness level influences the behavior of the test (mocking session)? + * How strictness influences the behavior of the test? *
* 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 our JUnit support ({@link MockitoJUnit}) or {@link MockitoSession}. *
* Adds following behavior: *
+ * 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 extends 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
+ * @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 extends Throwable> toBeThrown, Class extends Throwable>... 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..9088399aaa
--- /dev/null
+++ b/src/main/java/org/mockito/stubbing/LenientStubber.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+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()}
+ *
+ * @since 2.20.0
+ */
+@NotExtensible
+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
+
- * 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..cba0917f97 100644
--- a/src/main/java/org/mockito/stubbing/Stubbing.java
+++ b/src/main/java/org/mockito/stubbing/Stubbing.java
@@ -4,9 +4,12 @@
*/
package org.mockito.stubbing;
+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;
/**
* Stubbing declared on the mock object.
@@ -50,4 +53,13 @@ public interface Stubbing extends Answer {
* @since 2.2.3
*/
boolean wasUsed();
+
+ /**
+ * Informs about the {@link Strictness} level of this stubbing.
+ * For more information about setting strictness for stubbings see {@link Mockito#lenient()}.
+ *
+ * @since 2.20.0
+ */
+ @Incubating
+ 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/internal/junit/UnusedStubbingsFinderTest.java b/src/test/java/org/mockitousage/internal/junit/UnusedStubbingsFinderTest.java
index b03d5d7959..0eb3d7d346 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,11 +86,29 @@ 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");
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/StrictnessPerMockTest.java b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java
new file mode 100644
index 0000000000..2dce3e05e4
--- /dev/null
+++ b/src/test/java/org/mockitousage/strictness/StrictnessPerMockTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+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 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;
+import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.withSettings;
+
+//TODO 792 also move other Strictness tests to this package (unless they already have good package)
+public class StrictnessPerMockTest {
+
+ MockitoSession mockito;
+ @Mock IMethods strictStubsMock;
+ IMethods lenientMock;
+
+ @Before
+ public void before() {
+ mockito = Mockito.mockitoSession().initMocks(this).strictness(Strictness.STRICT_STUBS).startMocking();
+ assertNull(lenientMock);
+ lenientMock = mock(IMethods.class, withSettings().lenient());
+ }
+
+ @Test
+ public void knows_if_mock_is_lenient() {
+ assertTrue(mockingDetails(lenientMock).getMockCreationSettings().isLenient());
+ assertFalse(mockingDetails(strictStubsMock).getMockCreationSettings().isLenient());
+ }
+
+ @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 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. ->"));
+ }
+
+ @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 '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");
+ }
+
+ @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..3838beef7a
--- /dev/null
+++ b/src/test/java/org/mockitousage/strictness/StrictnessPerStubbingTest.java
@@ -0,0 +1,271 @@
+/*
+ * 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.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.AdditionalAnswers;
+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.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;
+
+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
+ 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.simpleMethod(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 with no exception:
+ mock.simpleMethod(200);
+
+ //and stubbing works, too:
+ assertEquals("2", mock.simpleMethod(1));
+ 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() {
+ 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);
+ }
+
+ @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);
+ }
+ }).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;
+ }
+ void scream(String message) {
+ throw new RuntimeException(message);
+ }
+ }
+
+ @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 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 doAnswer_syntax() {
+ //when
+ lenient().doAnswer(AdditionalAnswers.returnsFirstArg()).when(mock).simpleMethod("1");
+
+ //then on lenient stubbing, we can call it with different argument:
+ mock.simpleMethod("200");
+
+ //and stubbing works, too:
+ assertEquals("1", mock.simpleMethod("1"));
+ }
+
+ @Test
+ 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 792: this assertion is duplicated with StrictnessPerMockTest
+ .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
+ when(mock.simpleMethod("1")).thenReturn("1");
+ lenient().when(mock.differentMethod("2")).thenReturn("2");
+
+ //and:
+ mock.simpleMethod("1");
+ mock.differentMethod("200"); // <- 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("1. ->")
+ .hasMessageContaining("2. [?]->");
+ //TODO 792: assertion duplicated with StrictnessPerMockTest
+ // and we should use assertions based on content of the exception rather than the string
+ }
+
+ @After
+ public void after() {
+ mockito.finishMocking();
+ }
+}
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");
+ }
+}
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
new file mode 100644
index 0000000000..3a323fb2f4
--- /dev/null
+++ b/src/test/java/org/mockitousage/strictness/StrictnessWithRulesTest.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 StrictnessWithRulesTest {
+
+ @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");
+ }
+}
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
- * 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(new RuntimeException("one")).
- * doThrow(new RuntimeException("two"))
- * .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 extends 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
- * @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 extends Throwable> toBeThrown, Class extends Throwable>... nextToBeThrown);
-
- /**
- * Use it for stubbing consecutive calls in {@link Mockito#doAnswer(Answer)} style:
- *
- * doThrow(RuntimeException.class).
- * doThrow(IllegalArgumentException.class)
- * .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:
- *
- * doAnswer(answerOne).
- * doAnswer(answerTwo)
- * .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.
- *
- * doNothing().
- * doThrow(new RuntimeException("two"))
- * .when(mock).someVoidMethod();
- *