JUnit5: How to assert several properties of an object with a single assert call?
Asked Answered
U

3

17

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.

Unstick answered 13/8, 2019 at 10:59 Comment(1)
This is still possible using JUnit 5 and Hamcrest but I also prefer the way David mentionedCarotenoid
T
30

With AssertJ, another option would be to use returns:

import static org.assertj.core.api.Assertions.from;

assertThat(product)
    .returns("Coat", from(Product::getName))
    .returns(true, from(Product::getAvailable))
     //
     // or without 'from()'
     //
    .returns(12, Product::getAmount)
    .returns(new BigDecimal("88.0"), 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));
Tungus answered 17/8, 2019 at 20:10 Comment(2)
Very interesting alternative (+1). I didn't know that. IHMO, for objects with many properties to assert, it makes sense. For other cases, probably less. Note that from is optional. It is a reading marker.Canorous
@Stefano Cordio Thanks for this suggestion. This is a great way to express assertions. I updated your sample as it wasn't compiling with the commas (and also to include the suggestion by @Canorous that from() is optional).Amii
C
19

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 Strings because their validity as field names are checked only at the runtime (known issue of reflection) and that these Strings 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.

Canorous answered 13/8, 2019 at 11:8 Comment(0)
B
7

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

Bone answered 13/8, 2019 at 13:12 Comment(1)
That is right. But that is really very verbose too.Canorous

© 2022 - 2024 — McMap. All rights reserved.