Repository: mockito/mockito
Issue: "description" not printing when verify args don't match

Mockito version 2.27.0

I have a test that uses `description` inside a `verify` method. The `description` is correctly printing when the test fails because we don't interact with the mock

```
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import org.junit.Test;

public class ExampleTest {
    @Test
    public void shouldFailWithDescription() {
        Dependency dependency = spy(Dependency.class);
        SystemUnderTest systemUnderTest = new SystemUnderTest();
        systemUnderTest.doSomething(dependency);
        verify(dependency, description("Failed to call doSomethingElse")).doSomethingElse(false);
    }

    static class SystemUnderTest {
        void doSomething(Dependency dependency) {
            // dependency.doSomethingElse(true); // <---- TEST FAILS BECAUSE WE DON'T INTERACT WITH MOCK
        }
    }

    static class Dependency {
        void doSomethingElse(boolean value) {
            // ...
        }
    }
}
```

Results in this failure message:

```
org.mockito.exceptions.base.MockitoAssertionError: Failed to call doSomethingElse <---- SEE OUR DESCRIPTION

Wanted but not invoked:
dependency.doSomethingElse(false);
-> at com.example.ExampleTest$Dependency.doSomethingElse(ExampleTest.java:28)
Actually, there were zero interactions with this mock.
```

In this case the test failed because there were no interactions with this mock. However if we update the SUT so that it calls the method with the wrong argument:

```
public class ExampleTest {
    @Test
    public void shouldFailWithDescription() {
        Dependency dependency = spy(Dependency.class);
        SystemUnderTest systemUnderTest = new SystemUnderTest();
        systemUnderTest.doSomething(dependency);
        verify(dependency, description("Failed to call doSomethingElse")).doSomethingElse(false);
    }

    static class SystemUnderTest {
        void doSomething(Dependency dependency) {
             dependency.doSomethingElse(true);  // <---- TEST FAILS BECAUSE WE PASS WRONG ARGS
        }
    }

    static class Dependency {
        void doSomethingElse(boolean value) {
            // ...
        }
    }
}
```

The test still fails. But this time it **does not include the description**:

```
Argument(s) are different! Wanted: <----- WE SHOULD SEE DESCRIPTION
dependency.doSomethingElse(false);
-> at com.example.ExampleTest$Dependency.doSomethingElse(ExampleTest.java:28)
Actual invocation has different arguments:
dependency.doSomethingElse(true);
-> at com.example.ExampleTest$SystemUnderTest.doSomething(ExampleTest.java:21)

Comparison Failure: 
Expected :dependency.doSomethingElse(false);
Actual   :dependency.doSomethingElse(true);
```

Issue comments:
Not pretty but this seems to work if I use a `VerificationListener` and manually log the error:


```
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoFramework;
import org.mockito.listeners.VerificationListener;

public class ExampleTest {
    @Test
    public void shouldFailWithDescription() {
        MockitoFramework framework = Mockito.framework();
        String failureMessage = "Failed to call doSomethingElse";

        // not all test failures will print the description so we have to manually log it =(
        VerificationListener listener = verificationEvent -> {
            if (verificationEvent.getVerificationError() != null) {
                System.out.println("FAILURE: " + failureMessage);
            }
        };
        framework.addListener(listener);

        Dependency dependency = spy(Dependency.class);
        SystemUnderTest systemUnderTest = new SystemUnderTest();
        systemUnderTest.doSomething(dependency);
        verify(dependency, description(failureMessage)).doSomethingElse(false);

        // not sure if we need to remove for next test, or if mockito will auto-clear
        framework.removeListener(listener);
    }

    static class SystemUnderTest {
        void doSomething(Dependency dependency) {
            dependency.doSomethingElse(true);  // <---- TEST FAILS BECAUSE WE PASS WRONG ARGS
        }
    }

    static class Dependency {
        void doSomethingElse(boolean value) {
            // ...
        }
    }
}
```

now prints out 

```
FAILURE: Failed to call doSomethingElse

Argument(s) are different! Wanted:
dependency.doSomethingElse(false);
-> at com.example.ExampleTest$Dependency.doSomethingElse(ExampleTest.java:47)
Actual invocation has different arguments:
dependency.doSomethingElse(true);
-> at com.example.ExampleTest$SystemUnderTest.doSomething(ExampleTest.java:40)

Comparison Failure: 
Expected :dependency.doSomethingElse(false);
Actual   :dependency.doSomethingElse(true);
```
I would like to analyze this issue and eventually provide fixes. 
I can also verify the same issue.
Seems like a bug. Please submit a PR. Thanks!
The cause of this is that Reporter.argumentsAreDifferent() can return

-  an org.mockito.exceptions.verification.opentest4j.ArgumentsAreDifferent, a subclass of org.opentest4j.AssertionFailedError
- an org.mockito.exceptions.verification.junit.ArgumentsAreDifferent, a subclass of junit.framework.ComparisonFailure

according to whether opentest or junit is available in the classpath. Neither of these, crucially, extends MockitoAssertionError. Hence the exception is not caught in Description.verify and wrapped in a MockitoAssertionError with the description. That would only happen if neither opentest nor junit were in the classpath.

I think the catch block there should also catch these other exceptions and handle them in a similar way to include the description text.
It's extending those because of tooling and IDE support, I believe. In IntelliJ IDEA for example, this error is displayed really nice. You can see and compare the differences on the arguments.
The various ArgumentsAreDifferent exceptions come from org.mockito.internal.junit.ExceptionFactory, which deals solely with that condition. The header comment of MockitoAssertionError suggests it should be the base of all verification errors triggered by verify() methods. So maybe some refactoring is required. 

I don't know enough to say whether the solution is to handle the exceptions which are actually being thrown or change to throw subclasses of MockitoAssertionError which would then fix the issue.
Created a PR with a possible solution: https://github.com/mockito/mockito/pull/1949

Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/mockito/exceptions/base/MockitoAssertionError.java b/src/main/java/org/mockito/exceptions/base/MockitoAssertionError.java
index a3e046c479..5e463c7288 100644
--- a/src/main/java/org/mockito/exceptions/base/MockitoAssertionError.java
+++ b/src/main/java/org/mockito/exceptions/base/MockitoAssertionError.java
@@ -46,6 +46,18 @@ public MockitoAssertionError(MockitoAssertionError error, String message) {
         unfilteredStackTrace = error.getUnfilteredStackTrace();
     }
 
+    /**
+     * Creates a copy of the given assertion error with the custom failure message prepended.
+     * @param error The assertion error to copy
+     * @param message The custom message to prepend
+     * @since 3.3.13
+     */
+    public MockitoAssertionError(AssertionError error, String message) {
+        super(message + "\n" + error.getMessage());
+        unfilteredStackTrace = error.getStackTrace();
+        super.setStackTrace(unfilteredStackTrace);
+    }
+
     public StackTraceElement[] getUnfilteredStackTrace() {
         return unfilteredStackTrace;
     }
diff --git a/src/main/java/org/mockito/internal/verification/Description.java b/src/main/java/org/mockito/internal/verification/Description.java
index d9c133e03d..7732ceb98e 100644
--- a/src/main/java/org/mockito/internal/verification/Description.java
+++ b/src/main/java/org/mockito/internal/verification/Description.java
@@ -41,6 +41,8 @@ public void verify(VerificationData data) {
 
         } catch (MockitoAssertionError e) {
             throw new MockitoAssertionError(e, description);
+        } catch (AssertionError e) {
+            throw new MockitoAssertionError(e, description);
         }
     }
 }


Test patch:
diff --git a/src/test/java/org/mockito/DescriptionTest.java b/src/test/java/org/mockito/DescriptionTest.java
new file mode 100644
index 0000000000..32565e4b6c
--- /dev/null
+++ b/src/test/java/org/mockito/DescriptionTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito;
+
+import static org.mockito.Mockito.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.exceptions.base.MockitoAssertionError;
+
+/**
+ * Tests for https://github.com/mockito/mockito/issues/1712
+ */
+public class DescriptionTest {
+    @Rule public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void verify_method_not_called_should_include_description_in_report() {
+        final String description = "Failed to call doSomethingElse";
+        expectedException.expect(MockitoAssertionError.class);
+        expectedException.expectMessage(description);
+
+        Dependency dependency = spy(Dependency.class);
+        SystemUnderTest systemUnderTest = new SystemUnderTest();
+        systemUnderTest.doNothing(dependency);
+        verify(dependency, description(description)).doSomethingElse(false);
+    }
+
+    @Test
+    public void
+            verify_method_called_with_unexpected_argument_should_include_description_in_report() {
+        final String description = "Failed to call doSomethingElse with expected argument";
+        expectedException.expect(MockitoAssertionError.class);
+        expectedException.expectMessage(description);
+
+        Dependency dependency = spy(Dependency.class);
+        SystemUnderTest systemUnderTest = new SystemUnderTest();
+        systemUnderTest.doSomething(dependency);
+        verify(dependency, description(description)).doSomethingElse(false);
+    }
+
+    static class SystemUnderTest {
+        @SuppressWarnings("unused")
+        void doNothing(Dependency dependency) {}
+
+        void doSomething(Dependency dependency) {
+            dependency.doSomethingElse(true);
+        }
+    }
+
+    static class Dependency {
+        @SuppressWarnings("unused")
+        void doSomethingElse(boolean value) {}
+    }
+}
diff --git a/src/test/java/org/mockito/internal/verification/DescriptionTest.java b/src/test/java/org/mockito/internal/verification/DescriptionTest.java
index 5444ca342f..224aa02f06 100644
--- a/src/test/java/org/mockito/internal/verification/DescriptionTest.java
+++ b/src/test/java/org/mockito/internal/verification/DescriptionTest.java
@@ -51,4 +51,28 @@ public void verification_failure_should_prepend_expected_message() {
             assertEquals(expectedResult, e.getMessage());
         }
     }
+
+    /**
+     * Test of verify method, of class Description. This test validates that the custom message is prepended to the
+     * error message when verification fails and throws a Throwable which is not a MockitoAssertionError.
+     */
+    @Test
+    public void verification_failure_throwing_AssertionError_should_prepend_expected_message() {
+        String failureMessage = "message should be prepended to the original message";
+        String exceptionMessage = "original error message";
+        String expectedResult = failureMessage + "\n" + exceptionMessage;
+        AssertionError error = new AssertionError(exceptionMessage);
+        doThrow(error).when(mockVerificationMode).verify(mockVerificationData);
+
+        Description instance = new Description(mockVerificationMode, failureMessage);
+
+        try {
+            instance.verify(mockVerificationData);
+            verify(mockVerificationMode).verify(mockVerificationData);
+            fail("Should not have made it this far");
+
+        } catch (MockitoAssertionError e) {
+            assertEquals(expectedResult, e.getMessage());
+        }
+    }
 }