Repository: mockito/mockito
Issue: Improved exception message for wanted but not invoked
Labels: enhancement

Currently, Mockito prints following message when specific verification fails:

```
Wanted but not invoked:
mock.simpleMethod();
-> at ....

However, there were other interactions with this mock:
mock.otherMethod();
-> at ....
mock.booleanReturningMethod();
-> at ....
```

It would be useful to print the number of invocations:

```
Wanted but not invoked:
mock.simpleMethod();
-> at ....

However, there were exactly 2 interactions with this mock:
mock.otherMethod();
-> at ....
mock.booleanReturningMethod();
-> at ....
```


Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/internal/exceptions/Reporter.java b/src/main/java/org/mockito/internal/exceptions/Reporter.java
index 02e9a918fb..47505bf4cb 100644
--- a/src/main/java/org/mockito/internal/exceptions/Reporter.java
+++ b/src/main/java/org/mockito/internal/exceptions/Reporter.java
@@ -8,13 +8,7 @@
 import org.mockito.exceptions.base.MockitoAssertionError;
 import org.mockito.exceptions.base.MockitoException;
 import org.mockito.exceptions.misusing.*;
-import org.mockito.exceptions.verification.NeverWantedButInvoked;
-import org.mockito.exceptions.verification.NoInteractionsWanted;
-import org.mockito.exceptions.verification.SmartNullPointerException;
-import org.mockito.exceptions.verification.TooLittleActualInvocations;
-import org.mockito.exceptions.verification.TooManyActualInvocations;
-import org.mockito.exceptions.verification.VerificationInOrderFailure;
-import org.mockito.exceptions.verification.WantedButNotInvoked;
+import org.mockito.exceptions.verification.*;
 import org.mockito.internal.debugging.LocationImpl;
 import org.mockito.internal.exceptions.util.ScenarioPrinter;
 import org.mockito.internal.junit.JUnitTool;
@@ -36,6 +30,7 @@
 import java.util.List;
 
 import static org.mockito.internal.reporting.Pluralizer.pluralize;
+import static org.mockito.internal.reporting.Pluralizer.were_exactly_x_interactions;
 import static org.mockito.internal.util.StringJoiner.join;
 
 /**
@@ -319,7 +314,8 @@ public static MockitoAssertionError wantedButNotInvoked(DescribedInvocation want
         if (invocations.isEmpty()) {
             allInvocations = "Actually, there were zero interactions with this mock.\n";
         } else {
-            StringBuilder sb = new StringBuilder("\nHowever, there were other interactions with this mock:\n");
+            StringBuilder sb = new StringBuilder(
+                    "\nHowever, there " + were_exactly_x_interactions(invocations.size()) + " with this mock:\n");
             for (DescribedInvocation i : invocations) {
                 sb.append(i.toString())
                   .append("\n")
diff --git a/src/main/java/org/mockito/internal/reporting/Pluralizer.java b/src/main/java/org/mockito/internal/reporting/Pluralizer.java
index 5e597f32ec..1e8d0e3f4b 100644
--- a/src/main/java/org/mockito/internal/reporting/Pluralizer.java
+++ b/src/main/java/org/mockito/internal/reporting/Pluralizer.java
@@ -9,4 +9,12 @@ public class Pluralizer {
     public static String pluralize(int number) {
         return number == 1 ? "1 time" : number + " times";
     }
+
+    public static String were_exactly_x_interactions(int x) {
+        if (x == 1) {
+            return "was exactly 1 interaction";
+        } else {
+            return "were exactly " + x + " interactions";
+        }
+    }
 }


Test patch:
diff --git a/src/test/java/org/mockito/internal/reporting/PluralizerTest.java b/src/test/java/org/mockito/internal/reporting/PluralizerTest.java
index f35c3a8d0a..c2e1333fe9 100644
--- a/src/test/java/org/mockito/internal/reporting/PluralizerTest.java
+++ b/src/test/java/org/mockito/internal/reporting/PluralizerTest.java
@@ -12,11 +12,17 @@
 public class PluralizerTest extends TestBase {
 
     @Test
-    public void shouldGetPluralizedNumber() {
-        new Pluralizer();
+    public void pluralizes_number() {
         assertEquals("0 times", Pluralizer.pluralize(0));
         assertEquals("1 time", Pluralizer.pluralize(1));
         assertEquals("2 times", Pluralizer.pluralize(2));
         assertEquals("20 times", Pluralizer.pluralize(20));
     }
+
+    @Test
+    public void pluralizes_interactions() {
+        assertEquals("were exactly 0 interactions", Pluralizer.were_exactly_x_interactions(0));
+        assertEquals("was exactly 1 interaction", Pluralizer.were_exactly_x_interactions(1));
+        assertEquals("were exactly 100 interactions", Pluralizer.were_exactly_x_interactions(100));
+    }
 }
diff --git a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
index b5ea96b7b1..ee99529fab 100644
--- a/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
+++ b/src/test/java/org/mockito/internal/verification/checkers/MissingInvocationCheckerTest.java
@@ -55,7 +55,7 @@ public void shouldReportWantedButNotInvoked() {
 		exception.expect(WantedButNotInvoked.class);
 		exception.expectMessage("Wanted but not invoked:");
 		exception.expectMessage("mock.simpleMethod()");
-		exception.expectMessage("However, there were other interactions with this mock:");
+		exception.expectMessage("However, there was exactly 1 interaction with this mock:");
 		exception.expectMessage("mock.differentMethod();");
 
 		checker.check(invocations, wanted);
diff --git a/src/test/java/org/mockitousage/verification/OrdinaryVerificationPrintsAllInteractionsTest.java b/src/test/java/org/mockitousage/verification/OrdinaryVerificationPrintsAllInteractionsTest.java
index d352fabfa1..f6245a3841 100644
--- a/src/test/java/org/mockitousage/verification/OrdinaryVerificationPrintsAllInteractionsTest.java
+++ b/src/test/java/org/mockitousage/verification/OrdinaryVerificationPrintsAllInteractionsTest.java
@@ -21,14 +21,18 @@ public class OrdinaryVerificationPrintsAllInteractionsTest extends TestBase {
 
     @Test
     public void shouldShowAllInteractionsOnMockWhenOrdinaryVerificationFail() throws Exception {
+        //given
         firstInteraction();
         secondInteraction();
-        
+
+        verify(mock).otherMethod(); //verify 1st interaction
         try {
+            //when
             verify(mock).simpleMethod();
             fail();
         } catch (WantedButNotInvoked e) {
-            assertContains("However, there were other interactions with this mock", e.getMessage());
+            //then
+            assertContains("However, there were exactly 2 interactions with this mock", e.getMessage());
             assertContains("firstInteraction(", e.getMessage());
             assertContains("secondInteraction(", e.getMessage());
         }