Choosing the Right Matcher: HasItems vs Contains vs ContainsInAnyOrder

Snippet of programming code in IDE
Published on

Choosing the Right Matcher: HasItems vs Contains vs ContainsInAnyOrder

When working with testing frameworks in Java, especially in the realm of unit testing with libraries like Hamcrest or Mockito, properly asserting the correctness of your code is crucial. Three common matchers that often cause confusion among developers are hasItems, contains, and containsInAnyOrder. In this blog post, we'll explore each of these matchers, discuss their differences, and provide practical examples to help you choose the right matcher for your specific situation.

Understanding the Matchers

1. HasItems

hasItems is a matcher that checks whether a collection contains a specific set of elements, regardless of their order. It is particularly useful when you want to ensure that required elements are present in the collection but the order does not matter.

Example:

import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertThat;

import java.util.List;
import org.junit.Test;

public class MatcherExample {
    @Test
    public void testHasItems() {
        List<String> actualList = List.of("apple", "banana", "orange");

        assertThat(actualList, hasItems("banana", "orange"));
    }
}

Why Use hasItems?
Use hasItems when you care only about the presence of elements and not their order. This flexibility makes it perfect for scenarios where the element order is irrelevant.

2. Contains

contains is a matcher that checks whether a collection contains a specific sequence of elements in the same order. This matcher is instrumental when the sequence is crucial to the integrity of the output.

Example:

import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertThat;

import java.util.List;
import org.junit.Test;

public class MatcherExample {
    @Test
    public void testContains() {
        List<String> actualList = List.of("java", "python", "javascript");

        assertThat(actualList, contains("java", "python", "javascript"));
    }
}

Why Use contains?
Use contains when the order of elements is significant. This matcher verifies not only the presence of each element but also the expected sequence.

3. ContainsInAnyOrder

containsInAnyOrder is a matcher that checks whether a collection contains a specific set of elements, allowing for any order. It is useful when the element count and presence are important, but the sequence does not matter.

Example:

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;

import java.util.List;
import org.junit.Test;

public class MatcherExample {
    @Test
    public void testContainsInAnyOrder() {
        List<String> actualList = List.of("car", "bike", "bus");

        assertThat(actualList, containsInAnyOrder("bus", "car", "bike"));
    }
}

Why Use containsInAnyOrder?
Use containsInAnyOrder when the presence of every specified element and their count is essential, but the order in which they appear is not. It's perfect for situations like testing unordered sets or bags.

Comparison of Matchers

To summarize, here's a quick comparison of the three matchers:

| Matcher | Order Matter? | Presence Check | Use Case | |-----------------------|----------------|----------------|------------------------------------------------| | hasItems | No | Yes | Checking for the presence of elements | | contains | Yes | Yes | Checking for a specific ordered sequence | | containsInAnyOrder | No | Yes | Checking for presence and counting, order agnostic |

When to Use Each Matcher

Understanding how to choose the right matcher depends on the specific assertions you are making in your unit tests:

  • Use hasItems when you want to perform a simple existence check across elements and their order does not matter. This is often used in tests with collections where the order does not impact functionality.

  • Use contains when you need to validate the exact order of a list. This is common in scenarios where the output must match a predefined sequence, such as in certain sorting algorithms or pipeline processes.

  • Use containsInAnyOrder when verifying collections like Sets or other unordered collections where the order doesn't matter, but you still want to ensure all required elements are present.

The Closing Argument

In unit testing, choosing the right matcher can enhance the clarity and effectiveness of your assertions. Understanding the distinctions among hasItems, contains, and containsInAnyOrder can significantly reduce confusion and improve your testing strategy.

For further reading on unit testing in Java, consider the following resources:

Choosing the appropriate matcher according to the context of your tests can make your unit tests both clearer and more robust, thereby enhancing the overall quality and maintainability of your codebase. Happy testing!