Add declarative equivalent of JUnit's assertThrows
peterdemaeyer opened this issue · comments
Since version 4.13, JUnit has added assertThrows
, with typical JUnit style "expected first argument, actual second argument".
Hamcrest does not have a declarative equivalent.
As a developer, I want to have a declarative equivalent of Junit's assertThrows
, so that I can consistently use Hamcrest's declarative syntax when writing unit tests.
So, given the JUnit syntax:
assertThrows(Throwable.class, () -> methodThatThrows())
I would like to be able to write something like this in Hamcrest syntax:
assertThat(() -> methodThatThrows(), throws(instanceOf(Throwable.class)));
Of course, throws
is a reserved keyword in Java, so that would have to become for example doesThrow
instead.
The functionality I desire could really benefit from Java 1.8 lambda expressions, while Hamcrest is still on Java 1.7.
I'll prepare a fix for Java 1.7 nonetheless.
I've prepared a fix that adds the desired functionality.
- Added
<T extends Throwable> MatcherAssert.assertThat(String reason, Executable executable, Throws<T> doesThrow)
. - Added similar convenience method without reason.
Executable
is similar to JUnit's class with the same name, but I gave Hamcrest its own not to be tied down to a JUnit-specific dependency and in addition Hamcrest's is not a@FunctionalInterface
because it needs to be Java 1.7 compliant.- To avoid signature conflicts with other
assertThat
methods,Throws
is not aMatcher
itself, but is a distinct type. - Added multiple factory methods for throwable matchers to
Matchers
, backed by aThrows
class, in line with Hamcrest design, likedoesThrow
,throwsInstanceOf
etc. - Settled for
doesThrow
becausethrows
is a reserved Java keyword. - Took great care to make sure the descriptive phrasing of matches and mismatches is on par with Hamcrest's standards.
- Added Javadoc + new unit tests covering the new behavior.
Illustration of use (using Java 1.8 lambda expressions):
assertThat(() -> methodCallThatThrowsException(), doesThrow(instanceOf(Exception.class));
// Shorthand for the above
assertThat(() -> methodCallThatThrowsException(), throwsInstanceOf(Exception.class));
// Match throwable's cause rather than throwable itself
assertThat(() -> methodCallThatThrowsExceptionWithCause(), doesThrow(becauseOf(instanceOf(CauseException.class)));
// Match throwable's message
assertThat(() -> methodCallThatThrowsExceptionWithMessage(), doesThrow(withMessage(startsWith("start of message"))));
I created a PR for my changes, but it doesn't link correctly.
Not sure what is wrong, I could use some guidance here.