How to compare lists in any order without null fields in assertj?

2 min read 29-09-2024
How to compare lists in any order without null fields in assertj?


When working with Java, you might find yourself needing to compare lists that can be in any order and may contain null fields. This can pose a challenge, especially when using assertions in your tests. AssertJ provides a powerful way to handle this comparison effectively.

Understanding the Problem Scenario

The problem at hand is how to compare two lists in a way that disregards the order of the elements and ignores any null fields. Here is a snippet of code that may be trying to achieve this but falls short:

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

List<String> list1 = Arrays.asList("a", "b", null, "c");
List<String> list2 = Arrays.asList("c", "a", "b", null);

// Original assertion that does not account for nulls or order
assertThat(list1).containsExactlyInAnyOrder(list2);

In the code above, the use of containsExactlyInAnyOrder fails if any null values are present in either list, leading to potential false negatives.

Correct Approach

To compare the two lists correctly, you need to filter out the null values from both lists before performing the assertion. Here's how you can accomplish this:

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

List<String> list1 = Arrays.asList("a", "b", null, "c");
List<String> list2 = Arrays.asList("c", "a", "b", null);

// Filter out null values and assert the lists contain exactly the same elements, irrespective of order
assertThat(list1.stream().filter(Objects::nonNull).collect(Collectors.toList()))
    .containsExactlyInAnyOrderElementsOf(list2.stream().filter(Objects::nonNull).collect(Collectors.toList()));

Analysis and Explanation

  1. Filtering Nulls: The filter(Objects::nonNull) method is applied to both lists, which effectively removes any null entries. This ensures that we are comparing only the non-null values.

  2. Collecting Results: After filtering, we use collect(Collectors.toList()) to convert the streams back into lists. This makes them suitable for comparison using AssertJ's methods.

  3. Contains Exactly in Any Order: Finally, containsExactlyInAnyOrderElementsOf is used to verify that the two lists contain the same elements, without regard for their order.

Practical Example

Consider a real-world scenario where you have two lists of user IDs that you want to compare. One list may be fetched from a database, while the other is derived from a user interface input. It is critical to ensure that the user IDs match, regardless of their retrieval order and accounting for any null values.

List<Integer> dbUserIds = Arrays.asList(101, 102, null, 103);
List<Integer> inputUserIds = Arrays.asList(103, 101, 102, null);

// Assert equality while ignoring nulls and order
assertThat(dbUserIds.stream().filter(Objects::nonNull).collect(Collectors.toList()))
    .containsExactlyInAnyOrderElementsOf(inputUserIds.stream().filter(Objects::nonNull).collect(Collectors.toList()));

Conclusion

By using AssertJ along with Java Streams, you can effectively compare lists in any order while excluding null values. This enhances the reliability of your unit tests and ensures that they correctly reflect the expected outcomes.

Additional Resources

By following the steps outlined in this article, you can enhance your testing process and ensure that your lists are being accurately compared, regardless of their content or order. Happy testing!