1. Overview

We, as programmers, often write tests to make sure that our code works as intended. One of the standard practices in testing is to use assertions.

When we want to verify multiple properties of an object, we could write a bunch of assertions to get the work done.

However, in this tutorial, we’ll explore how to verify multiple properties in one single assert call.

2. Introduction to the Problem

In many cases, we need to check multiple properties of an object. Traditionally, this means writing separate assert statements for each property, which can make the code verbose and difficult to read.

However, a better way to do this is to use a single assert call for multiple properties. So next, let’s see how it’s done.

For a more straightforward demonstration, first, let’s look at a POJO class as an example:

class Product {
    private Long id;
    private String name;
    private String description;
    private boolean onSale;
    private BigDecimal price;
    private int stockQuantity;

    // constructor with all properties is omitted

    // getters and setters are omitted
}

The Product class has six properties. Let’s say we’ve implemented a program to produce a Product instance. Usually, we compare the generated Product instance to an expected object to assert if the program works, such as assertEquals(EXPECTED_PRODUCT, myProgram.createProduct()).

However, in our program, the id and description are not predictable. In other words, we consider the program does the job correctly if we can verify that the rest four fields (name, onSale, price, and stockQuantity) hold the expected values.

Next, let’s create a Product object as the expected result:

Product EXPECTED = new Product(42L, "LG Monitor", "32 inches, 4K Resolution, Ideal for programmers", true, new BigDecimal("429.99"), 77);

For simplicity, we won’t really implement a method to create a Product object. Instead, let’s simply create a Product instance to hold the required values, as our focus is on how to assert the four properties in one single statement:

Product TO_BE_TESTED = new Product(-1L, "LG Monitor", "dummy value: whatever", true, new BigDecimal("429.99"), 77);

So next, let’s see how to organize the assertions.

3. Using JUnit5’s assertAll()

JUnit is one of the most popular unit test frameworks. The latest version, JUnit 5, has brought many new features. For example, assertAll() is one of them.

JUnit 5’s assertAll() method takes a list of assertions, and all of them will be executed in a single call. Further, if any of the assertions fail, the test will fail, and all the failures will be reported.

Next, let’s group property assertions in one single assertAll() call:

assertAll("Verify Product properties",
  () -> assertEquals(EXPECTED.getName(), TO_BE_TESTED.getName()),
  () -> assertEquals(EXPECTED.isOnSale(), TO_BE_TESTED.isOnSale()),
  () -> assertEquals(EXPECTED.getStockQuantity(), TO_BE_TESTED.getStockQuantity()),
  () -> assertEquals(EXPECTED.getPrice(), TO_BE_TESTED.getPrice()));

As we can see, the assertAll() method groups four assertions in one call. It’s worth mentioning that the price field is of type BigDecimal. We use assertEquals() to verify the BigDecimal object’s value and scale. 

We’ve achieved our goal. However, if we look at the code carefully, inside the assertAll() body, we still have four assertions, even though they are in the lambda expression format. Therefore, the code is still a bit verbose.

Next, let’s see other approaches to asserting these four properties in one call.

4. Using AssertJ’s extracting() and containsExactly()

AssertJ is a powerful Java library that provides a fluent and intuitive API for writing assertions in tests. It provides the extracting() method that allows us only to extract our required properties’ values from an object. The extracted values are stored in a list. Then, AssertJ offers other methods to verify the list. For example, we can use containsExactly() to verify that the actual group contains exactly the given values in order and nothing else.

Next, let’ assemble extracting() and containsExactly():

assertThat(TO_BE_TESTED)
  .extracting("name", "onSale", "stockQuantity", "price")
  .containsExactly(EXPECTED.getName(), EXPECTED.isOnSale(), EXPECTED.getStockQuantity(), EXPECTED.getPrice());

As we’ve seen, AssertJ’s extracting() and containsExactly() allow us to write more concise and expressive assertions.

As the code above shows, passing property names as strings to the extracting() method is pretty straightforward. However, as the names are plain strings, they could contain typos. Moreover, if we renamed the properties, the test method still compiles without a problem. We won’t see the problem until we run the test. Also, it might take some time to find the naming problem finally.

Therefore, AssertJ supports passing the getter method references instead of property names to extracting():

assertThat(TO_BE_TESTED)
  .extracting(Product::getName, Product::isOnSale, Product::getStockQuantity,Product::getPrice)
  .containsExactly(EXPECTED.getName(), EXPECTED.isOnSale(), EXPECTED.getStockQuantity(), EXPECTED.getPrice());

5. Using AssertJ’s returns() and from()

AssertJ provides a rich set of assertions for various requirements. We’ve learned to use extracting() and containsExactly() to verify multiple properties in one assert call. In our example, we’d check four properties. However, we may want to verify ten properties in the real world. As the number of to-be-checked properties grows, the assertion line becomes difficult to read. Also, writing such a long assert line is error-prone.

Next, let’s see an alternative approach using AssertJ’s returns() and from() methods. The usage is pretty straightforward: assertThat(ToBeTestedObject).returns(Expected, from(FunctionToGetTheValue)).

So, the returns() method verifies the object under test returns the Expected value from the given function FunctionToGetTheValue.

Next, let’s apply this approach to verify the Product object:

assertThat(TO_BE_TESTED)
  .returns(EXPECTED.getName(), from(Product::getName))
  .returns(EXPECTED.isOnSale(), from(Product::isOnSale))
  .returns(EXPECTED.getStockQuantity(), from(Product::getStockQuantity))
  .returns(EXPECTED.getPrice(), from(Product::getPrice));

As we can see, the code is fluent and easy to read. Further, we won’t get lost even if we need to verify many properties.

It’s worth mentioning that AssertJ offers the doesNotReturn() method to verify the from() result doesn’t match the expected value. Furthermore, we can use doesNotReturn() and returns() in the same assertion.

Finally, let’s write a one-line assertion that mixes the returns() and doesNotReturn() methods:

assertThat(TO_BE_TESTED)
  .returns(EXPECTED.getName(), from(Product::getName))
  .returns(EXPECTED.isOnSale(), from(Product::isOnSale))
  .returns(EXPECTED.getStockQuantity(), from(Product::getStockQuantity))
  .returns(EXPECTED.getPrice(), from(Product::getPrice))
  .doesNotReturn(EXPECTED.getId(), from(Product::getId))
  .doesNotReturn(EXPECTED.getDescription(), from(Product::getDescription));

6. Conclusion

Using a single assert call to test multiple properties provides many benefits, such as improved readability, less error-prone, better maintainability, and so on.

In this article, we’ve learned three approaches to verifying multiple properties in one assert call through examples:

  • JUnit5 – assertAll()
  • AssertJ – extracting() and containsExactly()
  • AssertJ – returns(), doesNotReturn(), and from()

As usual, all code snippets presented in the article are available over on GitHub.