I would like to propose adding AssertJ <https://assertj.github.io/doc/> as
a test dependency and therefore have it available for writing
unit/distributed/any test assertions.

In addition to the examples mentioned on the AssertJ docs page (allows to
do elaborate and comprehensible assertions on Collections, String, and *custom
assertions*), here's an example of a dtest I was looking at, that could be
translated to AssertJ syntax, just to give an idea of how the syntax would
apply:

*JUnit asserts*:
try {
   [...]
} catch (Exception e) {
    Assert.assertTrue(e instanceof RuntimeException);
    RuntimeException re = ((RuntimeException) e);
    Assert.assertTrue(re.getCause() instanceof ReadTimeoutException);
    ReadTimeoutException rte = ((ReadTimeoutException) e.getCause());
    Assert.assertTrue(rte.getMessage().contains("blabla")
                      && rte.getMessage().contains("andblablo"));
}

*AssertJ style:*
try {
    [...]
} catch (Exception e) {
    Assertions.assertThat(e)
            .isInstanceOf(RuntimeException.class)
            .hasCauseExactlyInstanceOf(ReadTimeoutException.class)
            .hasMessageContaining("blabla")
            .hasMessageContaining("andblablo");
}

The syntax is more explicit and more comprehensible, but more importantly,
when one of the JUnit assertTrue() fails, you don't know *why*, you only
know that the resulting boolean expression is false.
If a failure happened with the assertJ tests, the failure would say "Exception
did not contain expected message, expected "blabla", actual "notblabla""
(same for a lot of other situations), this makes debugging a failure, after
a test ran and failed much easier. With JUnit asserts you would have to
additionally add a message explaining what the expected value is *and* what the
actual value is, for each assert that is more complex than a assertEquals
on a number, I suppose. I have seen a lot of tests so far that only test
the expected behavior via assertTrue and does not show the incorrect values
when the test fails, which would come for free with AssertJ.

Other examples randomly picked from the test suite:

*org.apache.cassandra.repair.RepairJobTest#testNoTreeRetainedAfterDistance:*
Replace assertion:
assertTrue(messages.stream().allMatch(m -> m.verb() == Verb.SYNC_REQ));
With:
assertThat(messages)
    .extracting(Message::verb)
    .containsOnly(Verb.SYNC_REQ);

As a result, if any of the messages is not a Verb.SYNC_REQ, the test
failure will show the actual "Verb"s of messages.

Replace:
assertTrue(millisUntilFreed < TEST_TIMEOUT_S * 1000);
With:
assertThat(millisUntilFreed)
    .isLessThan(TEST_TIMEOUT_S * 1000);

Same effect if the condition is not satisfied, more explicit error message
explaining why the test failed.

AssertJ also allows Custom assertions which are also very useful and could
potentially be leveraged in the future.

This would only touch on the tests' assertions, the rest of the test setup
and execution remains untouched (still uses JUnit for the test execution).

Thanks.

--
Kévin Gallardo.

Reply via email to