Test Object's multiple properties in Single Assert Test Object's multiple properties in Single Assert

JUnit 5, Hamcrest, AssertJ

Page content

In this article, we will learn how to assert Object’s multiple properties in a single assert in JUnit 5, Hamcrest, and AssertJ assertion libraries.

Overview

We usually write a unit test to verify the expected output from a piece of code. When the expected output is an Object, there are two ways to verify the properties of an Object:-

  1. Write an assertion for each property of an object and verify it individually
  2. Group the assertion for all properties of an object together and verify them collectively

The collective assertion of object properties is more human-readable and maintainable. Let’s see how we can group assertions using JUnit, Hamcrest and AssertJ.

Group the Assertions

Let’s say, we want to verify Product Object, which has various types of properties, ranging from Long, String, Boolean, Integer, BigDecimal to List.

public class Product {
    private Long id;
    private String name;
    private Boolean onSale;
    private Integer stockQuantity;
    private BigDecimal price;
    private List<String> labels;
}

Junit 5 - assertAll

We can collectively assert the Object’s properties using assertAll() in Junit 5. Let’s look at the example usage:-

  1. Verify the value of each Object’s property using assertEquals()
  2. Verify the various Object’s property conditions using assertNotNull() and assertTrue()
assertAll(messge,
    () -> assertFn(expected, actual),
    () -> assertFn(...),
    () -> assertFn(...)
)
import static org.junit.jupiter.api.Assertions.*;

@Test
public void givenObject_testAllProperties() {
    // Object to test
    Product product = new Product(1L, "Office Desk", true, 50, new BigDecimal("599.99"), Arrays.asList("Wooden", "Electric"));

    // 1. Verify property values collectively
    assertAll("Verify All Product Property Values",
        () -> assertEquals(1L, product.getId()),
        () -> assertEquals("Office Desk", product.getName()),
        () -> assertEquals(true, product.getOnSale()),
        () -> assertEquals(50, product.getStockQuantity()),
        () -> assertEquals("599.99", product.getPrice().toString()),
        () -> assertEquals(Arrays.asList("Wooden", "Electric"), product.getLabels()));

    // 2. Test property conditions collectively
    assertAll("Verify Product Property Conditions",
        () -> assertNotNull(product), 
        () -> assertNotNull(product.getId()),
        () -> assertTrue(product.getName().contains("Desk")),  // Product name contains 'Desk'
        () -> assertTrue(product.getOnSale()),  // Product on sale
        () -> assertTrue(product.getStockQuantity() > 0),  // Stock is available
        () -> assertTrue(product.getPrice().compareTo(new BigDecimal(1000.0)) < 0),  // Price under 1000
        () -> assertTrue(product.getLabels().contains("Wooden")));  // Looking for 'Wooden' Desk
    }

Hamcrest - allOf

We can combine the Hamcrest matchers using allOf() to collectively assert the Object’s properties.

assertThat(message, object, allOf(matcher1, matcher2, ...))

Let’s look a the example usage:-

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

@Test
public void givenObject_testAllProperties() {
    // Object to test
    Product product = new Product(1L, "Office Desk", true, 50, new BigDecimal("599.99"), Arrays.asList("Wooden", "Electric"));

    // Verify property values collectively
    assertThat("Verify All Product Property Values", product, allOf(
            hasProperty("id", equalTo(1L)),
            hasProperty("name", equalTo("Office Desk")),
            hasProperty("onSale", is(true)),
            hasProperty("stockQuantity", equalTo(50)),
            hasProperty("price", is(new BigDecimal("599.99"))),
            hasProperty("labels", equalTo(List.of("Wooden", "Electric")))));

    // Test property conditions collectively
    assertThat("Verify Product Property Conditions", product, allOf(
            notNullValue(),
            hasProperty("id", notNullValue()),
            hasProperty("name", endsWith("Desk")),  // Product name endsWith 'Desk'
            hasProperty("onSale", is(true)),  // Product on sale
            hasProperty("stockQuantity", greaterThan(0)),  // Stock is available
            hasProperty("price", lessThan(new BigDecimal(1000.0))),  // Price under 1000
            hasProperty("labels", hasItem("Electric"))));  // Looking for 'Electric' Desk
}

AssertJ - extracting..containsExactly

In AssertJ, we can collectively assert the Object’s properties by extracting them as a List using extracting() and then chain them with containsExactly() to verify the:-

  1. value of each Object’s property extracted from extracting() by passing field names
  2. result of each Object’s property condition extracted from extracting() by passing conditions
assertThat(object)
    .describedAs(message)
    .extracting(fieldName1/condition1, fieldName2/condition2, ...)
    .containsExactly(value1/result1, value2/result2, ...)

Let’s look at the example usage:-

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

@Test
public void givenObject_testAllProperties_extracting_containsExactly(){
    // Object to test
    Product product = new Product(1L, "Office Desk", true, 50, new BigDecimal("599.99"), Arrays.asList("Wooden", "Electric"));

    // 1. Verify property values collectively
    assertThat(product)
            .describedAs("Verify All Product Property Values")
            .extracting("id", "name", "onSale", "stockQuantity", "price", "labels")
            .containsExactly(1L, "Office Desk", true, 50, new BigDecimal("599.99"), Arrays.asList("Wooden", "Electric"));

    // 2. Test property conditions collectively
    assertThat(product)
            .describedAs("Verify Product Property Conditions")
            .extracting(Product::getId, 
                Product::getName, 
                Product::getOnSale, 
                p -> p.getStockQuantity() > 0, 
                p -> p.getPrice().compareTo(new BigDecimal(1000.0)) < 0, 
                p -> p.getLabels().contains("Wooden"))
            .containsExactly(1L, "Office Desk", true, true, true, true);

}

AssertJ - returns..from

In AssertJ, we can collectively assert the Object’s property by chaining the returns() for each property together. We extract the:-

  1. value from Object’s property by passing the field name in from()
  2. result from Object’s property condition by passing the condition in from()
assertThat(object)
    .describedAs(message)
    .returns(expected, from(fieldNameOrCondition))
    .returns(...)
    .returns(...)

Let’s look at the example usage:-

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

@Test
public void givenObject_testAllProperties_returns_from(){
    // Object to test
    Product product = new Product(1L, "Office Desk", true, 50, new BigDecimal("599.99"), Arrays.asList("Wooden", "Electric"));

    // 1. Verify property values collectively
    assertThat(product)
            .describedAs("Verify All Product Property Values")
            .returns(1L, from(Product::getId))
            .returns("Office Desk", from(Product::getName))
            .returns(true, from(Product::getOnSale))
            .returns(50, from(Product::getStockQuantity))
            .returns(new BigDecimal("599.99"), from(Product::getPrice))
            .returns(Arrays.asList("Wooden", "Electric"), from(Product::getLabels));

    // 2. Test property conditions collectively
    assertThat(product)
            .describedAs("Verify Product Property Conditions")
            .isNotNull()
            .hasNoNullFieldsOrProperties()
            .returns(true, from(p -> p.getName().contains("Desk")))  // Product name endsWith 'Desk'
            .returns(true, from(Product::getOnSale))  // Product on sale
            .returns(true, from(p -> p.getStockQuantity() > 0))  // Stock is available
            .returns(true, from(p -> p.getPrice().compareTo(new BigDecimal(1000.0)) < 0))  // Price under 1000
            .returns(true, from(p -> p.getLabels().contains("Wooden")));  // Looking for 'Wooden' Desk
}