Asserting properties on list elements with assertJ

CoronA picture CoronA · Dec 31, 2017 · Viewed 15.3k times · Source

I have a working hamcrest assertion:

assertThat(mylist, contains(
  containsString("15"), 
  containsString("217")));

The intended behavior is:

  • mylist == asList("Abcd15", "217aB") => success
  • myList == asList("Abcd15", "218") => failure

How can I migrate this expression to assertJ. Of course there exist naive solutions, like asserting on the first and second value, like this:

assertThat(mylist.get(0)).contains("15");
assertThat(mylist.get(1)).contains("217");

But these are assertions on the list elements, not on the list. Trying asserts on the list restricts me to very generic functions. So maybe it could be only resolved with a custom assertion, something like the following would be fine:

assertThat(mylist).elements()
  .next().contains("15")
  .next().contains("217")

But before I write a custom assert, I would be interested in how others would solve this problem?

Edit: One additional non-functional requirement is, that the test should be easily extendible by additional contstraints. In Hamcrest it is quite easy to express additional constraints, e.g.

assertThat(mylist, contains(
  emptyString(),                                     //additional element
  allOf(containsString("08"), containsString("15")), //extended constraint
  containsString("217")));                           // unchanged

Tests being dependent on the list index will have to be renumbered for this example, Tests using a custom condition will have to rewrite the complete condition (note that the constraints in allOf are not restricted to substring checks).

Answer

Joel Costigliola picture Joel Costigliola · Jan 1, 2018

For this kind of assertions Hamcrest is superior to AssertJ, you can mimic Hamcrest with Conditions but you need to write them as there are none provided out of the box in AssertJ (assertJ philosphy is not to compete with Hamcrest on this aspect).

In the next AssertJ version (soon to be released!), you will be able to reuse Hamcrest Matcher to build AssertJ conditions, example:

Condition<String> containing123 = new HamcrestCondition<>(containsString("123"));

// assertions succeed
assertThat("abc123").is(containing123);
assertThat("def456").isNot(containing123);

As a final note, this suggestion ...

assertThat(mylist).elements()
                  .next().contains("15")
                  .next().contains("217")

... unfortunately can't work because of generics limitation, although you know that you have a List of String, Java generics are not powerful enough to choose a specific type (StringAssert) depending on another (String), this means you can only perform Object assertion on the elements but not String assertion.

-- edit --

Since 3.13.0 one can use asInstanceOf to get specific type assertions, this is useful if the declared type is Object but the runtime type is more specific.

Example:

// Given a String declared as an Object
Object value = "Once upon a time in the west";

// With asInstanceOf, we switch to specific String assertion by specifying the InstanceOfAssertFactory for String
assertThat(value).asInstanceOf(InstanceOfAssertFactories.STRING)
                 .startsWith("Once");`

see https://assertj.github.io/doc/#assertj-core-3.13.0-asInstanceOf