hamcrest / JavaHamcrest

Java (and original) version of Hamcrest

Home Page:http://hamcrest.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 a Matcher itself, but is a distinct type.
  • Added multiple factory methods for throwable matchers to Matchers, backed by a Throws class, in line with Hamcrest design, like doesThrow, throwsInstanceOf etc.
  • Settled for doesThrow because throws 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.