Testing Java Collections with AssertJ.

Overview


Testing Collections in Java? You can say “Not a big deal”. I agree, but, for example, a comparison of two Lists of custom objects can be painful and it is not the only problem you can run into. Let’s dive into fluent assertions java library “AssertJ”, that can make your life easier and your tests more reliable and readable. Of course, you can use it not only for testing Collections’, but in this article we will focus on testing Lists, Sets, and Maps and will use only the basic AssertJ module – “AssertJ-core”.

Adding dependencies and imports.


Let’s start from adding a dependency for AssertJ.
Gradle dependency:
testImplementation 'org.assertj:assertj-core:3.13.2'

Maven dependency:
<dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    <version>3.13.2</version>
    <scope>test</scope>
</dependency>

Latest version can be found here
Then add a static import to your test classes.
import static org.fest.assertions.Assertions.assertThat;

Testing Lists with AssertJ

We’re going to test List of Strings, Integers and custom POJOs. For testing List of custom objects lets create class Cat, that contains 3 fields(name, age, and breed), constructor and all getters and setters. Code of this class will be omitted for brevity.
import static org.assertj.core.api.Assertions.assertThat;

public class ListsAssertJTest {
  private List<String> colors;
  private List<Cat> cats;
  private List<Integer> numbers;

  @Before
  public void setUp() throws Exception {
    colors = new ArrayList<>();
    cats = new ArrayList<>();
    numbers = new ArrayList<>();

    cats.add(new Cat("Mia", 6, "Birman"));
    cats.add(new Cat("Totoro", 2, "Domestic"));
    cats.add(new Cat("Booboo", 10, "Maine Coon"));
  }

//  Testing the List<String>
  @Test
  public void assertThatColorsListContainsExpectedValues() {
    colors.add("white");
    colors.add("blue");
    colors.add("red");

//    Here we asserting if "containsExactly()" contains all exactly the same values
//    and in the same order as in the List "colors". In the "as" is the message-description,
//    that we'll see if the test will not pass.
//    We can put "as" before each method, for more readable Error messages.
    assertThat(colors)
        .as("List colors should contain exactly [\"blue\", \"white\", \"red\"]")
        .containsExactly("white", "blue", "red");

//    In this test we are checking if the List "colors" isn't empty, has size = 3,
//    does not have duplicates, contains specific values, starts with "red",
//    ends with "blue", etc.
//    It is pretty awesome, that it is obvious even for a non-technical person, isn't it?:)
    assertThat(colors)
        .isNotEmpty()
        .hasSize(3)
        .doesNotHaveDuplicates()
        .contains("white", "blue", "red")
        .endsWith("red")
        .startsWith("white")
        .containsSequence("white", "blue")
        .containsExactly("white", "blue", "red")
        .doesNotContain("black", "orange");
  }

//  Testing the List<Integer>
  @Test
  public void assertThatNumbersListContainsExpectedValues() {
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
    numbers.add(4);
    numbers.add(5);

//    the same assertion as in the test below, but for List of Integers
//      we're checking if List "numbers" is not empty, has size = 5,
//      doesn't have duplicates contains 3, 2, 1, 4 and 5 in any order,
//      ends with 5, starts with 1, and contains exactly 1, 2, 3, 4, 5.
    assertThat(numbers)
        .isNotEmpty()
        .hasSize(5)
        .doesNotHaveDuplicates()
        .contains(3, 2, 1, 4, 5)
        .endsWith(5)
        .startsWith(1)
        .containsSequence(3, 4)
        .containsExactly(1, 2, 3, 4, 5);
  }

//  Testing List<Cat>
  @Test
  public void assertThatCatsListContainsExpectedValues() {

//    asserting if List<Cat> cats is not empty, has size 3
    assertThat(cats).isNotEmpty().hasSize(3);

//    "extracting" is using for extracting from object specific field.
//    In this case "name". You can either use method reference or
//    lambda expression in "extracting".
//    "contains" is using for checking if there are specific names
//    in the List "cats". The test will pass if our List "cats"
//    contains cats with given names in any order
    assertThat(cats).extracting(Cat::getName).contains("Totoro", "Mia");

//    same case as in the test above, but this test will pass
//    when at least one name from "contains"
//    will match the name of the cat from the List.
    assertThat(cats).extracting(Cat::getName).containsAnyOf("Mia", "Mary");

//    similar to the two tests above, but "containsExactly" will pass
//    only when it will contain all names of cats from the List "cats"
//    in the order that they are in that List.
//    "doesNotContainNull" checking if there are null objects in the "cats"
    assertThat(cats)
        .extracting(Cat::getName)
        .containsExactly("Mia", "Totoro", "Booboo")
        .doesNotContainNull();

//    in this test we're checking if it is empty, size and age of cats
    assertThat(cats).isNotEmpty().hasSize(3).extracting(Cat::getAge).contains(6, 2, 10);

//    in the test below we're checking if the List isn't empty,
//    has size = 3, and extracting cats' breeds.
//    Then we're checking if we don't have a cat  of a breed
//    "Sphynx" in the list, and if we do have cats of breeds
//    "Birman", "Domestic", and "Maine Coon"
    assertThat(cats)
        .isNotEmpty()
        .hasSize(3)
        .extracting(cat -> cat.getBreed())
        .doesNotContain("Sphynx")
        .contains("Birman", "Domestic", "Maine Coon");
  }

  @Test
  public void assertIfSpecificObjectsOfTheListEqualsToGiven() {
//    creating a cat "mia" with the same values, that the cats.get(0)
    Cat mia = new Cat("Mia", 6, "Birman");

//    asserting that cats.get(0) has the all fields with the same values as "mia"
    assertThat(cats.get(0)).usingRecursiveComparison().isEqualTo(mia);

//    changing the age of "mia"
    mia.setAge(1);

//    asserting that the cats.get(0) has the same values as "mia" ignoring the field "age"
    assertThat(cats.get(0)).usingRecursiveComparison().ignoringFields("age").isEqualTo(mia);

//    we can also create Predicate and assert if the actual object matches the given predicate
    assertThat(cats.get(0)).matches(c -> c.getAge() > 3);
  }
}

Testing Sets with AssertJ


Take a look at “SetsAssertJTest” and methods I was using to test Set of Strings and Set of custom POJOs.
import static org.assertj.core.api.Assertions.assertThat;

public class SetsAssertJTest {
  private Set<String> colors;
  private Set<Cat> cats;

  @Before
  public void setUp() throws Exception {
    colors = new HashSet<>();
    cats = new HashSet<>();
  }

  @Test
  public void assertThatColorsListContainsExpectedValues() {
    colors.add("white");
    colors.add("blue");
    colors.add("red");

    //    Testing Sets is pretty similar to testing Lists.
    //    In this test we checking if the Set "colors" is not empty, has size 3, contains "blue" and
    // "red",
    //    contains any of these 3 colors("blue", "yellow", "brown"),
    //    contains everything from these colors("blue", "white", "red") in any order,
    //    doesn't contain sequence of these colors("yellow", "brown"),
    //    and also doesn't contain "black" color.

    assertThat(colors)
        .isNotEmpty()
        .hasSize(3)
        .contains("blue", "red")
        .containsAnyOf("blue", "yellow", "brown")
        .containsExactlyInAnyOrder("blue", "white", "red")
        .doesNotContainSequence("yellow", "brown")
        .doesNotContain("black");
  }

  @Test
  public void assertThatCatsListContainsExpectedValues() {
    cats.add(new Cat("Mia", 6, "Birman"));
    cats.add(new Cat("Totoro", 2, "Domestic"));
    cats.add(new Cat("Booboo", 10, "Maine Coon"));

    //    In this test we're checking if the Set of Cat objects isn't empty,
    //    has size 3, then with help of "extracting" we extract names from cats' objects
    //    using method references. After that we're checking if names contain
    //    "Mia", "Totoro", and "Booboo", and using "allMatch" we create Predicate
    //    and checking if all cat names have length greater than 2,
    //    and using "anyMatch" create a Predicate for checking if
    //    any of cats' names starts from "To".
    //    Also we're checking if The Set doesn't contain a cat
    //    with the name "Bella".

    assertThat(cats)
        .isNotEmpty()
        .hasSize(3)
        .extracting(Cat::getName)
        .contains("Mia", "Totoro", "Booboo")
        .allMatch(n -> n.length() > 2)
        .anyMatch(n -> n.startsWith("To"))
        .doesNotContain("Bella");

    //    In this test we extracting age of cats and using "filteredOn"
    //   filter cats and keep only that objects, that have age greater than 2.
    //    Then we're extracting names and chacking if they contain only "Mia" and "Booboo".
    assertThat(cats)
        .filteredOn(c -> c.getAge() > 2)
        .extracting(Cat::getName)
        .containsOnly("Mia", "Booboo");

    //    In this test we checking if the cats is exactly instance of HashSet
    //    also extracting cat's breeds using lambda expression and checking
    //    if they contain "Birman", "Domestic" and "Maine Coon".
    assertThat(cats)
        .isExactlyInstanceOf(HashSet.class)
        .extracting(cat -> cat.getBreed())
        .contains("Birman", "Domestic", "Maine Coon");
  }
}

Testing Maps with AssertJ.


Let’s look at java class below and techniques I use to test Map.

Conclusion

Using “AssertJ” you can test Java Collections easily, even if it is Collection of custom objects. It has a huge variety of methods that you can use for this purpose, that’ll make your tests bullet-proof. AssertJ also provides helpful error messages, improves tests’ readability and is easy to use within your favorite IDE. In this article, we saw the most useful assertion methods and even created different types of matchers. But there are even more methods in AssertJ you can explore, find them here. Happy testing!