'JUnit5: How to assert several properties of an object with a single assert call?
I want to assert several properties of an object with a single assert call.
With JUnit 4 and Hamcrest I would have written something like this:
assertThat(product, allOf(
hasProperty("name", is("Coat")),
hasProperty("available", is(true)),
hasProperty("amount", is(12)),
hasProperty("price", is(new BigDecimal("88.0")))
));
Q: How to assert several properties in a single assert call using JUnit 5 and AssertJ? Or, alternatively, what is the best way of doing that in JUnit 5 universe.
Note: I can of course create an object with all needed properties and perform
assertThat(actualProduct, is(expectedProduct))
but that is not the point.
Solution 1:[1]
With AssertJ, another option would be to use returns
:
assertThat(product)
.returns("Coat", from(Product::getName)),
.returns(true, from(Product::getAvailable)),
.returns(12, from(Product::getAmount)),
.returns(new BigDecimal("88.0"), from(Product::getPrice));
A bit more verbose but I find it easier to read compared to extracting
/contains
.
Note that from
is just an optional syntax sugar to improve readability.
Since 3.22.0, doesNotReturn
is also available. This can be useful for non-null fields where the expected value is not known in advance.
assertThat(product)
.returns("Coat", from(Product::getName)),
.returns(true, from(Product::getAvailable)),
.doesNotReturn(42, from(Product::getAmount)),
.doesNotReturn(null, from(Product::getPrice));
Solution 2:[2]
With AssertJ the closest is :
assertThat(product).extracting("name", "available", "amount", "price")
.containsExactly("Coat", true, 12, new BigDecimal("88.0"));
But I don't like to reference field names with String
s because their validity as field names are checked only at the runtime (known issue of reflection) and that these String
s may also be incorrectly updated during refactoring operations that we perform from the IDE.
While a little more verbose, I prefer that :
assertThat(product).extracting(Product::getName, Product::getAvailable,
Product::getAmount, Product::getPrice)
.containsExactly("Coat", true, 12, new BigDecimal("88.0"));
The advantage of AssertJ over Hamcrest that you quote is that that is really fluent : so in most of cases, you need a single import : import org.assertj.core.api.Assertions;
and for collection assertions, sometimes that : org.assertj.core.groups.Tuple;
Here JUnit 5 or 4; it doesn't really matter since you will only use JUnit as a test runner for a very simple case and leave AssertJ to perform the assertions.
Or, alternatively, what is the best way of doing that in JUnit 5 universe.
JUnit 5 (as the 4th version) doesn't provide features for flexible and fluent assertions. So doing it with the best way of JUnit 5 is bound to produce much more boiler plate code : as many assertions as fields to assert or overriding equals()/hashCode()
override that you want to avoid for fair reasons.
Solution 3:[3]
You could use Assertions.assertAll
:
assertAll("product",
() -> assertEquals("Coat", product.getName()),
() -> assertTrue(product.isAvaikable())
);
assertAll can take as many individual asserts as you like. Here’s the link to the section in the user guide: https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | johanneslink |