Mockito Argument Matchers

Introduction

In this chapter, we will learn about argument matchers using Mockito. Argument matchers allow you to specify flexible and dynamic conditions for method arguments when stubbing and verifying method calls. This makes your tests more versatile and easier to maintain.

What are Argument Matchers?

Argument matchers enable you to define criteria for method arguments without specifying exact values. Mockito provides several built-in matchers to help you create flexible and dynamic stubs and verifications.

Common Argument Matchers

1. any()

Matches any argument of the specified type.

Example

when(calculatorService.add(anyInt(), anyInt())).thenReturn(10);

2. eq()

Matches the exact argument value.

Example

when(calculatorService.subtract(eq(10), eq(5))).thenReturn(5);

3. argThat()

Matches an argument that satisfies the specified condition.

Example

when(calculatorService.multiply(argThat(x -> x > 0), argThat(y -> y > 0))).thenReturn(20);

4. isA()

Matches any argument that is an instance of the specified class.

Example

when(calculatorService.process(isA(String.class))).thenReturn("Processed");

5. isNull()

Matches a null argument.

Example

when(calculatorService.process(isNull())).thenReturn("Null Processed");

6. notNull()

Matches a non-null argument.

Example

when(calculatorService.process(notNull())).thenReturn("Not Null Processed");

7. same()

Matches an argument that is the same instance as the specified object.

Example

when(calculatorService.process(same(someObject))).thenReturn("Same Object Processed");

8. startsWith()

Matches a string argument that starts with the specified prefix.

Example

when(calculatorService.process(startsWith("Hello"))).thenReturn("Hello World");

9. endsWith()

Matches a string argument that ends with the specified suffix.

Example

when(calculatorService.process(endsWith("World"))).thenReturn("Hello World");

10. contains()

Matches a string argument that contains the specified substring.

Example

when(calculatorService.process(contains("Hello"))).thenReturn("Hello World");

Example: CalculatorService Class and CalculatorServiceTest Class

Class Under Test: CalculatorService

public class CalculatorService {
    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }

    public int multiply(int a, int b) {
        return a * b;
    }

    public int divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Division by zero");
        }
        return a / b;
    }

    public String process(String input) {
        // Process the input string
        return "Processed: " + input;
    }
}

Test Class: CalculatorServiceTest

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

public class CalculatorServiceTest {

    @Test
    void testAddWithAnyInt() {
        // Create a mock object of CalculatorService
        CalculatorService calculatorService = mock(CalculatorService.class);

        // Stub the add method with argument matchers
        when(calculatorService.add(anyInt(), anyInt())).thenReturn(10);

        // Use the mock object
        int result = calculatorService.add(5, 3);

        // Assert the result
        assertEquals(10, result);

        // Verify the interaction
        verify(calculatorService).add(anyInt(), anyInt());
    }

    @Test
    void testSubtractWithSpecificMatchers() {
        // Create a mock object of CalculatorService
        CalculatorService calculatorService = mock(CalculatorService.class);

        // Stub the subtract method with specific argument matchers
        when(calculatorService.subtract(eq(10), eq(5))).thenReturn(5);

        // Use the mock object
        int result = calculatorService.subtract(10, 5);

        // Assert the result
        assertEquals(5, result);

        // Verify the interaction
        verify(calculatorService).subtract(eq(10), eq(5));
    }

    @Test
    void testMultiplyWithCustomMatchers() {
        // Create a mock object of CalculatorService
        CalculatorService calculatorService = mock(CalculatorService.class);

        // Stub the multiply method with custom argument matchers
        when(calculatorService.multiply(argThat(x -> x > 0), argThat(y -> y > 0))).thenReturn(20);

        // Use the mock object
        int result = calculatorService.multiply(4, 5);

        // Assert the result
        assertEquals(20, result);

        // Verify the interaction
        verify(calculatorService).multiply(argThat(x -> x > 0), argThat(y -> y > 0));
    }

    @Test
    void testProcessWithStringMatchers() {
        // Create a mock object of CalculatorService
        CalculatorService calculatorService = mock(CalculatorService.class);

        // Stub the process method with various string argument matchers
        when(calculatorService.process(startsWith("Hello"))).thenReturn("Hello World");
        when(calculatorService.process(endsWith("World"))).thenReturn("Hello World");
        when(calculatorService.process(contains("Test"))).thenReturn("Contains Test");

        // Use the mock object
        String result1 = calculatorService.process("Hello there");
        String result2 = calculatorService.process("Goodbye World");
        String result3 = calculatorService.process("This is a Test");

        // Assert the results
        assertEquals("Hello World", result1);
        assertEquals("Hello World", result2);
        assertEquals("Contains Test", result3);

        // Verify the interactions
        verify(calculatorService).process(startsWith("Hello"));
        verify(calculatorService).process(endsWith("World"));
        verify(calculatorService).process(contains("Test"));
    }

    @Test
    void testDivideWithAnyInt() {
        // Create a mock object of CalculatorService
        CalculatorService calculatorService = mock(CalculatorService.class);

        // Stub the divide method with argument matchers
        when(calculatorService.divide(anyInt(), anyInt())).thenAnswer(invocation -> {
            int a = invocation.getArgument(0);
            int b = invocation.getArgument(1);
            if (b == 0) {
                throw new IllegalArgumentException("Division by zero");
            }
            return a / b;
        });

        // Use the mock object
        int result = calculatorService.divide(10, 2);

        // Assert the result
        assertEquals(5, result);

        // Verify the interaction
        verify(calculatorService).divide(anyInt(), anyInt());

        // Verify that division by zero throws an exception
        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            calculatorService.divide(10, 0);
        });

        assertEquals("Division by zero", exception.getMessage());
    }
}

Explanation

  1. Creating Mocks:

    CalculatorService calculatorService = mock(CalculatorService.class);
    

    This line creates a mock object of the CalculatorService class.

  2. Stubbing with anyInt() Matcher:

    when(calculatorService.add(anyInt(), anyInt())).thenReturn(10);
    

    This line stubs the add method to return 10 regardless of the integer arguments passed to it.

  3. Stubbing with eq() Matcher:

    when(calculatorService.subtract(eq(10), eq(5))).thenReturn(5);
    

    This line stubs the subtract method to return 5 only when the arguments are 10 and 5.

  4. Stubbing with argThat() Matcher:

    when(calculatorService.multiply(argThat(x -> x > 0), argThat(y -> y > 0))).thenReturn(20);
    

    This line stubs the multiply method to return 20 only when both arguments are greater than 0.

  5. Stubbing with String Matchers:

    when(calculatorService.process(startsWith("Hello"))).thenReturn("Hello World");
    when(calculatorService.process(endsWith("World"))).thenReturn("Hello World");
    when(calculatorService.process(contains("Test"))).thenReturn("Contains Test");
    

    These lines stub the process method to return specific strings when the input string starts with "Hello", ends with "World", or contains "Test".

  6. Stubbing with anyInt() Matcher and Custom Logic:

    when(calculatorService.divide(anyInt(), anyInt())).thenAnswer(invocation -> {
        int a = invocation.getArgument(0);
        int b = invocation.getArgument(1);
        if (b == 0) {
            throw new IllegalArgumentException("Division by zero");
        }
        return a / b;
    });
    

    This line stubs the divide method to return the result of division unless the divisor is 0,

in which case it throws an exception.

Conclusion

Argument matchers in Mockito provide powerful and flexible ways to define conditions for method arguments during stubbing and verification. By using matchers like any(), eq(), argThat(), and various string matchers, you can create versatile and dynamic tests that cover a wide range of scenarios. Understanding and utilizing these matchers will help you write more effective and maintainable tests.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top