Hamcrest
02 / 02

Collection Matchers & Custom Matchers

Hamcrest: Collection Matchers & Custom Matchers

Collection Matchers

@Test
void collectionMatchers() {
    List<String> fruits = List.of("apple", "banana", "cherry");

    assertThat(fruits, hasSize(3));
    assertThat(fruits, hasSize(greaterThan(0)));  // composes with other matchers

    // hasItem: at least this element exists, anywhere, ignoring the rest
    assertThat(fruits, hasItem("banana"));
    assertThat(fruits, hasItems("apple", "cherry"));

    // contains: the ENTIRE collection must match, in this exact order --
    // much stricter than hasItem
    assertThat(fruits, contains("apple", "banana", "cherry"));

    // containsInAnyOrder: exact elements required, order doesn't matter --
    // appropriate for a Set or non-deterministic iteration order
    assertThat(Set.of("a", "b", "c"), containsInAnyOrder("c", "b", "a"));

    assertThat(List.of(), is(empty()));
    assertThat(fruits, everyItem(not(nullValue())));
}

Writing a Custom Matcher

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

// A domain-specific matcher: is this order actually valid to submit?
public class IsValidOrder extends TypeSafeMatcher<Order> {

    @Override
    protected boolean matchesSafely(Order order) {
        return !order.getItems().isEmpty()
            && order.getTotal().compareTo(BigDecimal.ZERO) > 0
            && order.getShippingAddress() != null;
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("a valid order with items, a positive total, and a shipping address");
    }

    @Override
    protected void describeMismatchSafely(Order order, Description mismatchDescription) {
        mismatchDescription.appendText("was ").appendValue(order);
    }

    public static IsValidOrder isValidOrder() {
        return new IsValidOrder();
    }
}

// Usage -- reads naturally, and failures explain exactly what was expected
import static com.example.matchers.IsValidOrder.isValidOrder;

@Test
void orderShouldBeValid() {
    Order order = buildOrder();
    assertThat(order, isValidOrder());
}

Hamcrest vs AssertJ

  • Both solve the same problem: more expressive, better-diagnostic assertions than raw assertEquals/assertTrue.

  • Hamcrest: assertThat(list, hasSize(3)) -- many separately-imported static matcher functions.

  • AssertJ: assertThat(list).hasSize(3).contains("a") -- fluent method chaining, often more IDE-autocomplete-discoverable per type.

  • Both integrate with JUnit 4/5 and TestNG the same way -- picking one is largely a team-style preference, not a functional difference.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free