Skip to content

Commit 756a3cf

Browse files
authored
Add description of matchers to potential mismatch (#3760)
Fixes #2468
1 parent 58ba445 commit 756a3cf

4 files changed

Lines changed: 111 additions & 9 deletions

File tree

mockito-core/src/main/java/org/mockito/internal/exceptions/Reporter.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import org.mockito.listeners.InvocationListener;
5757
import org.mockito.mock.MockName;
5858
import org.mockito.mock.SerializableMode;
59+
import org.mockito.stubbing.Stubbing;
5960

6061
/**
6162
* Reports verification and misusing errors.
@@ -1150,12 +1151,12 @@ public static void unncessaryStubbingException(List<Invocation> unused) {
11501151
}
11511152

11521153
public static void potentialStubbingProblem(
1153-
Invocation actualInvocation, Collection<Invocation> argMismatchStubbings) {
1154+
Invocation actualInvocation, Collection<Stubbing> argMismatchStubbings) {
11541155
StringBuilder stubbings = new StringBuilder();
11551156
int count = 1;
1156-
for (Invocation s : argMismatchStubbings) {
1157+
for (Stubbing s : argMismatchStubbings) {
11571158
stubbings.append(" ").append(count++).append(". ").append(s);
1158-
stubbings.append("\n ").append(s.getLocation()).append("\n");
1159+
stubbings.append("\n ").append(s.getInvocation().getLocation()).append("\n");
11591160
}
11601161
stubbings.deleteCharAt(stubbings.length() - 1); // remove trailing end of line
11611162

mockito-core/src/main/java/org/mockito/internal/junit/DefaultStubbingLookupListener.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void onStubbingLookup(StubbingLookupEvent event) {
4949
// If stubbing was not found for invocation it means that either the mock invocation was
5050
// not stubbed or
5151
// we have a stubbing arg mismatch.
52-
List<Invocation> argMismatchStubbings =
52+
List<Stubbing> argMismatchStubbings =
5353
potentialArgMismatches(event.getInvocation(), event.getAllStubbings());
5454
if (!argMismatchStubbings.isEmpty()) {
5555
mismatchesReported = true;
@@ -64,9 +64,9 @@ public void onStubbingLookup(StubbingLookupEvent event) {
6464
}
6565
}
6666

67-
private static List<Invocation> potentialArgMismatches(
67+
private static List<Stubbing> potentialArgMismatches(
6868
Invocation invocation, Collection<Stubbing> stubbings) {
69-
List<Invocation> matchingStubbings = new LinkedList<>();
69+
List<Stubbing> matchingStubbings = new LinkedList<>();
7070
for (Stubbing s : stubbings) {
7171
if (UnusedStubbingReporting.shouldBeReported(s)
7272
&& Objects.equals(
@@ -78,7 +78,7 @@ private static List<Invocation> potentialArgMismatches(
7878
&& !Objects.equals(
7979
s.getInvocation().getLocation().getSourceFile(),
8080
invocation.getLocation().getSourceFile())) {
81-
matchingStubbings.add(s.getInvocation());
81+
matchingStubbings.add(s);
8282
}
8383
}
8484
return matchingStubbings;

mockito-core/src/test/java/org/mockitousage/junitrule/StrictJUnitRuleTest.java

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package org.mockitousage.junitrule;
66

77
import static org.junit.Assert.assertEquals;
8+
import static org.mockito.ArgumentMatchers.argThat;
9+
import static org.mockito.ArgumentMatchers.eq;
810
import static org.mockito.BDDMockito.given;
911
import static org.mockito.BDDMockito.willReturn;
1012
import static org.mockito.Mockito.verify;
@@ -102,9 +104,9 @@ public void doAssert(Throwable t) {
102104
+ " mock.simpleMethod(15);\n"
103105
+ " -> at org.mockitousage.strictness.ProductionCode.simpleMethod(ProductionCode.java:0)\n"
104106
+ " - has following stubbing(s) with different arguments:\n"
105-
+ " 1. mock.simpleMethod(20);\n"
107+
+ " 1. mock.simpleMethod(20); stubbed with: [Returns: 20]\n"
106108
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument(StrictJUnitRuleTest.java:0)\n"
107-
+ " 2. mock.simpleMethod(30);\n"
109+
+ " 2. mock.simpleMethod(30); stubbed with: [Returns: 30]\n"
108110
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument(StrictJUnitRuleTest.java:0)\n"
109111
+ "Typically, stubbing argument mismatch indicates user mistake when writing tests.\n"
110112
+ "Mockito fails early so that you can debug potential problem easily.\n"
@@ -132,6 +134,101 @@ public void doAssert(Throwable t) {
132134
ProductionCode.simpleMethod(mock, 15);
133135
}
134136

137+
@Test
138+
public void fails_fast_when_stubbing_invoked_with_different_argument_using_matchers()
139+
throws Throwable {
140+
// expect
141+
rule.expectFailure(
142+
new SafeJUnitRule.FailureAssert() {
143+
public void doAssert(Throwable t) {
144+
Assertions.assertThat(t).isInstanceOf(PotentialStubbingProblem.class);
145+
assertEquals(
146+
filterLineNo(
147+
"\n"
148+
+ "Strict stubbing argument mismatch. Please check:\n"
149+
+ " - this invocation of 'simpleMethod' method:\n"
150+
+ " mock.simpleMethod(15);\n"
151+
+ " -> at org.mockitousage.strictness.ProductionCode.simpleMethod(ProductionCode.java:0)\n"
152+
+ " - has following stubbing(s) with different arguments:\n"
153+
+ " 1. mock.simpleMethod(20); stubbed with: [Returns: 20]\n"
154+
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument_using_matchers(StrictJUnitRuleTest.java:0)\n"
155+
+ " 2. mock.simpleMethod(30); stubbed with: [Returns: 30]\n"
156+
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument_using_matchers(StrictJUnitRuleTest.java:0)\n"
157+
+ " 3. mock.simpleMethod(\"40\"); stubbed with: [Returns: 40]\n"
158+
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument_using_matchers(StrictJUnitRuleTest.java:0)\n"
159+
+ "Typically, stubbing argument mismatch indicates user mistake when writing tests.\n"
160+
+ "Mockito fails early so that you can debug potential problem easily.\n"
161+
+ "However, there are legit scenarios when this exception generates false negative signal:\n"
162+
+ " - stubbing the same method multiple times using 'given().will()' or 'when().then()' API\n"
163+
+ " Please use 'will().given()' or 'doReturn().when()' API for stubbing.\n"
164+
+ " - stubbed method is intentionally invoked with different arguments by code under test\n"
165+
+ " Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).\n"
166+
+ "For more information see javadoc for PotentialStubbingProblem class."),
167+
filterLineNo(t.getMessage()));
168+
}
169+
});
170+
171+
// when stubbings in the test code:
172+
willReturn("10").given(mock).simpleMethod(eq(10)); // used
173+
willReturn("20").given(mock).simpleMethod(eq(20)); // unused
174+
willReturn("30").given(mock).simpleMethod(eq(30)); // unused
175+
willReturn("40").given(mock).simpleMethod(eq("40")); // unused
176+
177+
// then
178+
mock.otherMethod(); // ok, different method
179+
mock.simpleMethod(10); // ok, stubbed with this argument
180+
181+
// invocation in the code under test uses different argument and should fail immediately
182+
// this helps with debugging and is essential for Mockito strictness
183+
ProductionCode.simpleMethod(mock, 15);
184+
}
185+
186+
@Test
187+
public void fails_fast_when_stubbing_invoked_with_different_argument_using_argThat_matcher()
188+
throws Throwable {
189+
// expect
190+
rule.expectFailure(
191+
new SafeJUnitRule.FailureAssert() {
192+
public void doAssert(Throwable t) {
193+
Assertions.assertThat(t).isInstanceOf(PotentialStubbingProblem.class);
194+
assertEquals(
195+
filterLineNo(
196+
"\n"
197+
+ "Strict stubbing argument mismatch. Please check:\n"
198+
+ " - this invocation of 'forInteger' method:\n"
199+
+ " mock.forInteger(15);\n"
200+
+ " -> at org.mockitousage.strictness.ProductionCode.forInteger(ProductionCode.java:0)\n"
201+
+ " - has following stubbing(s) with different arguments:\n"
202+
+ " 1. mock.forInteger(<custom argument matcher>); stubbed with: [Returns: 20]\n"
203+
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument_using_argThat_matcher(StrictJUnitRuleTest.java:0)\n"
204+
+ " 2. mock.forInteger(<custom argument matcher>); stubbed with: [Returns: 30]\n"
205+
+ " -> at org.mockitousage.junitrule.StrictJUnitRuleTest.fails_fast_when_stubbing_invoked_with_different_argument_using_argThat_matcher(StrictJUnitRuleTest.java:0)\n"
206+
+ "Typically, stubbing argument mismatch indicates user mistake when writing tests.\n"
207+
+ "Mockito fails early so that you can debug potential problem easily.\n"
208+
+ "However, there are legit scenarios when this exception generates false negative signal:\n"
209+
+ " - stubbing the same method multiple times using 'given().will()' or 'when().then()' API\n"
210+
+ " Please use 'will().given()' or 'doReturn().when()' API for stubbing.\n"
211+
+ " - stubbed method is intentionally invoked with different arguments by code under test\n"
212+
+ " Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).\n"
213+
+ "For more information see javadoc for PotentialStubbingProblem class."),
214+
filterLineNo(t.getMessage()));
215+
}
216+
});
217+
218+
// when stubbings in the test code:
219+
willReturn("10").given(mock).forInteger(argThat(x -> x < 11)); // used
220+
willReturn("20").given(mock).forInteger(argThat(x -> x > 19)); // unused
221+
willReturn("30").given(mock).forInteger(argThat(x -> x > 29)); // unused
222+
223+
// then
224+
mock.otherMethod(); // ok, different method
225+
mock.forInteger(10); // ok, stubbed with this argument
226+
227+
// invocation in the code under test uses different argument and should fail immediately
228+
// this helps with debugging and is essential for Mockito strictness
229+
ProductionCode.forInteger(mock, 15);
230+
}
231+
135232
@Test
136233
public void verify_no_more_interactions_ignores_stubs() throws Throwable {
137234
// when stubbing in test:

mockito-core/src/test/java/org/mockitousage/strictness/ProductionCode.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ public static void simpleMethod(IMethods mock, String argument) {
1919
public static void simpleMethod(IMethods mock, int argument) {
2020
mock.simpleMethod(argument);
2121
}
22+
23+
public static void forInteger(IMethods mock, int argument) {
24+
mock.forInteger(argument);
25+
}
2226
}

0 commit comments

Comments
 (0)