Introduction
In this chapter, we will learn about stubbing methods with Mockito. Stubbing is a key concept in unit testing, allowing you to define the behavior of mock objects. This helps you isolate the code you are testing and control the responses of its dependencies.
What is Stubbing?
Stubbing is the process of specifying what mock objects should do when their methods are called. By defining the behavior of mock objects, you can control their interactions and ensure that your tests are reliable and predictable. Stubbing allows you to return specific values, throw exceptions, or execute custom logic when a method is called.
Stubbing Void Methods
Void methods do not return a value, but you can still control their behavior. You might want to stub void methods to do nothing, throw an exception, or perform some custom action.
Example: EmailService Class and EmailServiceTest Class
Class Under Test: EmailService
public class EmailService {
public void sendEmail(String recipient, String message) {
// Logic to send email
System.out.println("Sending email to " + recipient + ": " + message);
}
}
Test Class: EmailServiceTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
public class EmailServiceTest {
@Test
void testVoidMethod() {
// Create a mock object of EmailService
EmailService emailService = mock(EmailService.class);
// Stub the void method to do nothing
doNothing().when(emailService).sendEmail(anyString(), anyString());
// Call the void method
emailService.sendEmail("test@example.com", "Hello");
// Verify that the void method was called
verify(emailService).sendEmail("test@example.com", "Hello");
}
@Test
void testVoidMethod_ThrowException() {
// Create a mock object of EmailService
EmailService emailService = mock(EmailService.class);
// Stub the void method to throw an exception
doThrow(new RuntimeException("Exception occurred")).when(emailService).sendEmail(anyString(), anyString());
// Call the void method and handle the exception
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
emailService.sendEmail("test@example.com", "Hello");
});
assertEquals("Exception occurred", exception.getMessage());
}
}
Stubbing with Specific Arguments
You can stub methods to return specific values when called with particular arguments.
Example: Calculator Class and CalculatorTest Class
Class Under Test: Calculator
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
Test Class: CalculatorTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class CalculatorTest {
@Test
void testStubbingWithArguments() {
// Create a mock object of Calculator
Calculator calculator = mock(Calculator.class);
// Stub the add method with specific arguments
when(calculator.add(2, 3)).thenReturn(5);
// Use the mock object
int result = calculator.add(2, 3);
// Assert the result
assertEquals(5, result);
// Verify the interaction
verify(calculator).add(2, 3);
}
}
Running the Tests using IntelliJ IDEA
Stubbing with Argument Matchers
Argument matchers allow you to stub methods with flexible arguments. This is useful when you want to specify behavior for a range of inputs.
Example: Calculator Class and CalculatorTest Class
Class Under Test: Calculator
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
Test Class: CalculatorTest
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 CalculatorTest {
@Test
void testStubbingWithArgumentMatchers() {
// Create a mock object of Calculator
Calculator calculator = mock(Calculator.class);
// Stub the add method with argument matchers
when(calculator.add(anyInt(), anyInt())).thenReturn(10);
// Use the mock object
int result = calculator.add(4, 6);
// Assert the result
assertEquals(10, result);
// Verify the interaction
verify(calculator).add(anyInt(), anyInt());
}
}
Stubbing with Callbacks
You can use callbacks to stub methods with custom logic. This allows you to define dynamic behavior for mock objects.
Example: Calculator Class and CalculatorTest Class
Class Under Test: Calculator
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int subtract(int a, int b) {
return a - b;
}
}
Test Class: CalculatorTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class CalculatorTest {
@Test
void testStubbingWithCallbacks() {
// Create a mock object of Calculator
Calculator calculator = mock(Calculator.class);
// Stub the add method with a callback
when(calculator.add(anyInt(), anyInt())).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) {
int a = invocation.getArgument(0);
int b = invocation.getArgument(1);
return a + b;
}
});
// Use the mock object
int result = calculator.add(4, 6);
// Assert the result
assertEquals(10, result);
// Verify the interaction
verify(calculator).add(4, 6);
}
}
Explanation
- Stubbing with a Callback:
when(calculator.add(anyInt(), anyInt())).thenAnswer(new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocation) { int a = invocation.getArgument(0); int b = invocation.getArgument(1); return a + b; } });
This line stubs the
add
method with a callback that adds the two arguments and returns the result.
Conclusion
Stubbing methods with Mockito is a powerful way to control the behavior of mock objects in your tests. By understanding how to stub void methods, methods with specific arguments, methods with argument matchers, and methods with callbacks, you can create more flexible and effective tests. This will help you ensure that your code behaves as expected under various conditions.