Argument Matchers in Mockito: any(), eq(), argThat()

Illustration for Argument Matchers in Mockito: any(), eq(), argThat()
By Last updated:

When testing Java applications, we often want to verify method calls without caring about the exact arguments or by applying flexible conditions. Mockito’s argument matchers provide this power. Instead of hardcoding values, you can use matchers like any(), eq(), and argThat() to make tests more readable, maintainable, and adaptable.

In this tutorial, we’ll explore Mockito’s most common argument matchers, their real-world use cases, and best practices for writing reliable unit tests.


Why Argument Matchers Matter

  • Flexibility: Test behavior without tying tests to specific argument values.
  • Simplicity: Avoid verbose setup for parameters you don’t care about.
  • Resilience: Prevent fragile tests when method signatures evolve.
  • Edge Case Simulation: Match custom conditions using argThat().

Think of argument matchers as filters at airport security — they don’t check every detail, but only the conditions that matter.


Setting Up Mockito with JUnit 5

Maven

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>5.7.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>5.7.0</version>
  <scope>test</scope>
</dependency>

Gradle

testImplementation 'org.mockito:mockito-core:5.7.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.7.0'

Example: Order Service with Dependency

class PaymentGateway {
    boolean process(String userId, double amount) {
        return true;
    }
}

class OrderService {
    private final PaymentGateway gateway;

    OrderService(PaymentGateway gateway) {
        this.gateway = gateway;
    }

    boolean placeOrder(String userId, double amount) {
        return gateway.process(userId, amount);
    }
}

Using any()

any() matches any value of a given type.

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

class OrderServiceTest {

    @Test
    void shouldPlaceOrderWithAnyUser() {
        PaymentGateway gateway = mock(PaymentGateway.class);
        when(gateway.process(anyString(), anyDouble())).thenReturn(true);

        OrderService service = new OrderService(gateway);
        boolean result = service.placeOrder("user123", 100.0);

        assertTrue(result);
        verify(gateway).process(anyString(), anyDouble());
    }
}

Here, we don’t care about the exact user ID or amount — just that the method was called.


Using eq()

eq() ensures an exact match for specific arguments.

@Test
void shouldProcessExactAmount() {
    PaymentGateway gateway = mock(PaymentGateway.class);
    when(gateway.process(eq("user123"), eq(200.0))).thenReturn(true);

    OrderService service = new OrderService(gateway);
    boolean result = service.placeOrder("user123", 200.0);

    assertTrue(result);
    verify(gateway).process(eq("user123"), eq(200.0));
}

This test ensures only user123 with amount 200.0 works.


Using argThat()

argThat() allows custom conditions.

@Test
void shouldProcessHighValueOrders() {
    PaymentGateway gateway = mock(PaymentGateway.class);
    when(gateway.process(eq("vipUser"), argThat(amount -> amount > 1000))).thenReturn(true);

    OrderService service = new OrderService(gateway);
    boolean result = service.placeOrder("vipUser", 2000.0);

    assertTrue(result);
    verify(gateway).process(eq("vipUser"), argThat(amount -> amount > 1000));
}

This is useful for validating ranges, regex patterns, or complex object properties.


Best Practices for Argument Matchers

  • Use any() when you don’t care about specific arguments.
  • Use eq() for exact matches on critical values.
  • Use argThat() for custom validation logic.
  • Avoid mixing raw values and matchers — stick to one approach per method call.
  • Keep argThat() expressions readable; extract lambdas if needed.

Mockito + Testcontainers Example

Mockito is for unit testing, while Testcontainers help with integration testing. You might mock dependencies in unit tests but run real database containers for integration:

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;

class DatabaseIntegrationTest {

    @Test
    void shouldStartDatabase() {
        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
            postgres.start();
            assert postgres.isRunning();
        }
    }
}

Together, they ensure both speed (unit tests with mocks) and reliability (integration with containers).


Version Tracker

  • JUnit 4 → JUnit 5: Improved extension support for Mockito.
  • Mockito Updates: Argument matchers expanded with lambdas (argThat).
  • Testcontainers Growth: Wider ecosystem for integration alongside mocks.

Conclusion & Key Takeaways

Mockito’s argument matchers make tests flexible, expressive, and reliable. With any(), eq(), and argThat(), you can handle simple to complex scenarios easily while keeping your unit tests clean.

Key Takeaways:

  • Use any() when arguments don’t matter.
  • Use eq() for exact value checks.
  • Use argThat() for custom conditions.
  • Avoid mixing raw values and matchers.
  • Combine unit mocks with Testcontainers for robust testing.

FAQ

1. What’s the difference between unit and integration tests?
Unit tests isolate logic with mocks; integration tests use real systems.

2. How do I mock a static method in Mockito?
Use mockStatic() (Mockito 3.4+).

3. Can I mix matchers and raw values in Mockito?
No, use either all matchers or all exact values for a method call.

4. How do I test ranges of values?
Use argThat() with conditions like amount -> amount > 1000.

5. What’s the performance impact of argument matchers?
Negligible — they only affect test logic, not production code.

6. Can Mockito work with custom objects?
Yes, use argThat() for custom validations.

7. Should I always use matchers?
No, use them when arguments are not fixed or need flexible validation.

8. How do I debug failing argThat() matchers?
Log matcher conditions or extract them into readable methods.

9. Does Mockito work with JUnit 5?
Yes, via mockito-junit-jupiter extension.

10. Should I migrate from JUnit 4 to JUnit 5?
Yes, JUnit 5 provides better extensions and modern APIs.