Repository: assertj/assertj

Problem statement:
Describe actual Throwables when ThrowableAssert fails
### Summary
When some `Throwable`-related assertion fails the actual `Throwable` that failed the check is not described beyond what actually caused assertion failure (e.g. message mismatch). In non-trivial code when unexpected `Exception`s are thrown this makes debugging inconvenient.

Dumping more comprehensive (class, message, stack, perhaps even recursive info about causes) info about the actual `Throwable` could ease assertion failure investigation.

Relevant message factories (e.g. `ShouldHaveMessage`) already have access to actual `Throwable`.

It may be tempting to use `util.Throwables.ERROR_DESCRIPTION_EXTRACTOR` to describe throwables, but a more powerful mechanism would be appreciated. 
Providing precise information in case of assertion failure doesn't seem to be overkill (unlike as in #864)

### Error message to improve

- [x] ShouldHaveCause #2872
- [ ] ShouldHaveCauseExactlyInstance
- [ ] ShouldHaveCauseInstance
- [ ] ShouldHaveCauseReference - @weiyilei
- [ ] ShouldHaveNoCause
- [ ] ShouldHaveNoSuppressedExceptions
- [x] ShouldHaveRootCauseExactlyInstance #2910
- [x] ShouldHaveRootCauseInstance #2910
- [ ] ShouldHaveSuppressedException
- [ ] ShouldNotContainCharSequence 
- [ ] ShouldStartWith
- [ ] ShouldEndWith

If you are interested in contributing to this issue, select one error message, it will be assigned to you.



Hints:
do you mean printing the stack trace at the end of the error message ?

can you give an example of what you would ideally have ?
Example of my ideal message:
```java
Expecting Throwable message:
<"expected exception message">
but was:
<"some other unexpected exception">

Throwable that failed the check:
Exception in thread "main" java.lang.RuntimeException: some other unexpected exception
	at Test.functionThatIsSupposedToThrowExpectedException(Test.java:119)
	at Test.main(Test.java:69)
Caused by: java.lang.RuntimeException: unexpected exception in recurseToGenerateSomeStack
	at Test.recurseToGenerateSomeStack(Test.java:133)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.recurseToGenerateSomeStack(Test.java:138)
	at Test.functionThatIsSupposedToThrowExpectedException(Test.java:115)
	... 1 more
Caused by: java.lang.RuntimeException: the root cause of test failure
	at Test.f(Test.java:169)
	at Test.e(Test.java:164)
	at Test.d(Test.java:159)
	at Test.c(Test.java:154)
	at Test.b(Test.java:149)
	at Test.a(Test.java:144)
	at Test.recurseToGenerateSomeStack(Test.java:129)
	... 13 more
```

Such message allows to immediately learn the real cause of test failure.
Fair enough, @jakubzytka would you like to contribute it?
I have done it for the `hasMessage` assertion, it should also done for other throwable assertions.
Submitted #1630 to do the same for `hasMessageMatching` and `hasMessageFindingMatch` assertions.
Hello, is this issue still open ? I did try the various method asserting the messages in `AbstractThrowableAssert` but didn't find any case where the stacktrace is not displayed. Or can you point the specific methods to fix ? 
`Throwables.assertHasMessageContaining` doesn't describe the actual throwable (neither do some other functions that care about the message)

Hello, I did a first pull request to fix the hasMessageContaining and hasMessageContainingAll assertions, let me know if this is correct.

Next steps is to investigate and fix which methods can still be improved, here is a list to be refined of potential classes to be adapted:
```
// done in upper commits
ShouldHaveMessage 
ShouldHaveMessageFindingMatchRegex
ShouldHaveMessageMatchingRegex
ShouldHaveRootCause

// still need to adapt the stackTrace case
ShouldContainCharSequence.shouldContain 

// to be adapted (maybe doesn't apply)
ShouldEndWith
ShouldHaveCause
ShouldHaveCauseExactlyInstance
ShouldHaveCauseInstance
ShouldHaveCauseReference
ShouldHaveNoCause
ShouldHaveNoSuppressedExceptions
ShouldHaveRootCauseExactlyInstance
ShouldHaveRootCauseInstance
ShouldHaveSuppressedException
ShouldNotContainCharSequence
ShouldStartWith
```
Hi, I've created a PR fixing the `hasCauseInstanceOf` and `hasCauseExactlyInstanceOf`. Please let me know if there is any problem:-).
Is anybody working on this? If not, can I?
thanks @weiyilei, I have assigned you `ShouldHaveCauseReference`, you can look at `ShouldHaveCauseInstance` for reference.
> thanks @weiyilei, I have assigned you `ShouldHaveCauseReference`, you can look at `ShouldHaveCauseInstance` for reference.

thank u bro, but I am really confused about what kind of reference will be required in this issue, can u give me an example plz
> thanks @weiyilei, I have assigned you `ShouldHaveCauseReference`, you can look at `ShouldHaveCauseInstance` for reference.

Hi bro, I still have no idea about what does the cause reference mean, as I know A.getcause() can get the throwable which lead to A, so what's its reference. :(
Besides, I want to know whether this issue is still open and remains any job to be finished, thanks.
Add more basic assertions for Local(Date|Time) #2541
> thanks @weiyilei, I have assigned you `ShouldHaveCauseReference`, you can look at `ShouldHaveCauseInstance` for reference.

should I use getStackTrace() or getSuppressed(), or import org.openjdk.jol.vm.VM and use VM.current().addressof()
ShouldHaveCauseInstance is the example
> ShouldHaveCauseInstance is the example

Hi, I am wondering if the content of ShouldHaveCauseReference_create_Test.java should be modified, or I should just fill the methods of ShouldHaveCauseReference according to the test content
I'm not sure I understand the question, if you modify ShouldHaveCauseReference you have to write the tests that proves your changes are working and thus you would need to add test cases in ShouldHaveCauseReference_create_Test
@weiyilei if you still don't understand what I'm saying it means you are not yet ready to contribute and you need a bit more experience before doing so.

The AssertJ team is working on its spare time maintaining this project, it's not that we don't want to help junior devs to contribute, it's just that we don't have the time to do so.
> I'm not sure I understand the question, if you modify ShouldHaveCauseReference you have to write the tests that proves your changes are working and thus you would need to add test cases in ShouldHaveCauseReference_create_Test

Hi bro, I have tried my best to understand ur requirements and finished my codes following the example of CauseInstance, I have a pull request and let me know if there is something I need to improve. thanks
@joel-costigliola Hi, I'm interested in this issue. Is there any available task for me to do?
Hi @joel-costigliola I created a PR to update the ShouldHaveCause with Stacktrace. Please let me know if there is a problem.
Hi @joel-costigliola, Thanks for reviewing my earlier PR. I have created a new one for ShouldHaveRootCauseInstance and ShouldHaveRootCauseExactlyInstance. Please let me know if there is any problem with the same. 

Patch:
diff --git a/assertj-core/src/main/java/org/assertj/core/error/ShouldNotContainCharSequence.java b/assertj-core/src/main/java/org/assertj/core/error/ShouldNotContainCharSequence.java
--- a/assertj-core/src/main/java/org/assertj/core/error/ShouldNotContainCharSequence.java
+++ b/assertj-core/src/main/java/org/assertj/core/error/ShouldNotContainCharSequence.java
@@ -69,7 +69,7 @@ public static ErrorMessageFactory shouldNotContain(Throwable actual, CharSequenc
                     "  %s%n" +
                     "not to contain:%n" +
                     "  %s%n" +
-                    "but found:%n" +
+                    "but did:%n" +
                     "%n" +
                     "Throwable that failed the check:%n" +
                     "%n" + escapePercent(getStackTrace(actual)); // to avoid AssertJ default String formatting


Test patch:
diff --git a/assertj-core/src/test/java/org/assertj/core/error/ShouldNotContainThrowable_create_Test.java b/assertj-core/src/test/java/org/assertj/core/error/ShouldNotContainThrowable_create_Test.java
--- a/assertj-core/src/test/java/org/assertj/core/error/ShouldNotContainThrowable_create_Test.java
+++ b/assertj-core/src/test/java/org/assertj/core/error/ShouldNotContainThrowable_create_Test.java
@@ -33,16 +33,16 @@ class ShouldNotContainThrowable_create_Test {
   @Test
   void should_create_error_message_with_escaping_percent() {
     // GIVEN
-    RuntimeException actual = new RuntimeException("You know nothing %");
+    RuntimeException actual = new RuntimeException("You know nothing % Jon Snow");
     // WHEN
-    String errorMessage = shouldNotContain(actual, "You know nothing % Jon Snow").create(new TestDescription("TEST"));
+    String errorMessage = shouldNotContain(actual, "You know nothing %").create(new TestDescription("TEST"));
     // THEN
     then(errorMessage).isEqualTo("[TEST] %n" +
                                  "Expecting throwable message:%n" +
-                                 "  \"You know nothing %%\"%n" +
-                                 "not to contain:%n" +
                                  "  \"You know nothing %% Jon Snow\"%n" +
-                                 "but found:%n" +
+                                 "not to contain:%n" +
+                                 "  \"You know nothing %%\"%n" +
+                                 "but did:%n" +
                                  "%n" +
                                  "Throwable that failed the check:%n" +
                                  "%n%s", getStackTrace(actual));
@@ -51,19 +51,19 @@ void should_create_error_message_with_escaping_percent() {
   @Test
   void should_create_error_message_with_several_values_not_found() {
     // GIVEN
-    RuntimeException actual = new RuntimeException("You know nothing");
-    String[] sequence = array("You", "know", "nothing", "Jon", "Snow");
-    Set<String> notFound = newSet("Jon", "Snow");
+    RuntimeException actual = new RuntimeException("You know nothing Jon Snow");
+    String[] sequence = array("You", "know", "nothing");
+    Set<String> found = newSet("You", "know", "nothing");
     // WHEN
-    String errorMessage = shouldNotContain(actual, sequence, notFound).create(new TestDescription("TEST"));
+    String errorMessage = shouldNotContain(actual, sequence, found).create(new TestDescription("TEST"));
     // THEN
     then(errorMessage).isEqualTo("[TEST] %n" +
                                  "Expecting throwable message:%n" +
-                                 "  \"You know nothing\"%n" +
+                                 "  \"You know nothing Jon Snow\"%n" +
                                  "not to contain:%n" +
-                                 "  [\"You\", \"know\", \"nothing\", \"Jon\", \"Snow\"]%n" +
+                                 "  [\"You\", \"know\", \"nothing\"]%n" +
                                  "but found:%n" +
-                                 "  [\"Jon\", \"Snow\"]%n" +
+                                 "  [\"You\", \"know\", \"nothing\"]%n" +
                                  "%n" +
                                  "Throwable that failed the check:%n" +
                                  "%n%s", getStackTrace(actual));