A little bit more briefly with static imports and checking both the class and the message of the cause exception:
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.hasProperty;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@Test
public void testThatThrowsNiceExceptionWithCauseAndMessages(){
expectedException.expect(RuntimeException.class );
expectedException.expectMessage("Exception message");
expectedException.expectCause(allOf(instanceOf(IllegalStateException.class),
hasProperty("message", is("Cause message"))) );
throw new RuntimeException("Exception message", new IllegalStateException("Cause message"));
}
You could even use the hasProperty matcher to assert nested causes or to test the "getLocalizedMessage" method.
Update::
Junit has migrated the assertThrows from JUnit 5 to 4. I would really recommended this options to make the test clearer (and easier to migrate to Junit5 in the future).
RuntimeException ex = assertThrows(RuntimeException.class, () -> {
throw new RuntimeException("Exception message", new IllegalStateException("Cause message"));
});
assertThat(ex.getMessage(), contains("Exception message"));
assertThat(ex.getMessage().getCause(), instanceOf(IllegalStateException.class),;
assertThat(ex.getMessage().getCause().getMessage(), is("Cause message"));