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
-
Creating Mocks:
CalculatorService calculatorService = mock(CalculatorService.class);
This line creates a mock object of the
CalculatorService
class. -
Stubbing with
anyInt()
Matcher:when(calculatorService.add(anyInt(), anyInt())).thenReturn(10);
This line stubs the
add
method to return10
regardless of the integer arguments passed to it. -
Stubbing with
eq()
Matcher:when(calculatorService.subtract(eq(10), eq(5))).thenReturn(5);
This line stubs the
subtract
method to return5
only when the arguments are10
and5
. -
Stubbing with
argThat()
Matcher:when(calculatorService.multiply(argThat(x -> x > 0), argThat(y -> y > 0))).thenReturn(20);
This line stubs the
multiply
method to return20
only when both arguments are greater than0
. -
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". -
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 is0
,
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.