Mocking a complex object graph with Mockito involves creating mock objects for each component of the graph and defining their behavior to simulate real interactions. Here's a step-by-step guide on how to achieve this:
1. Creating Mock Objects**
First, you need to create mock objects for each class in the object graph. Mockito provides several ways to do this:
- Using `@Mock` Annotation: This is typically used with JUnit tests. You annotate fields with `@Mock`, and Mockito initializes them automatically when you use `MockitoAnnotations.initMocks(this)` or with JUnit 5's `@ExtendWith(MockitoExtension.class)`.
java
@Mock
private Database database;
- Using `mock()` Method: You can manually create mock objects using the `mock()` method.
java
Database database = Mockito.mock(Database.class);
2. Defining Mock Behavior**
After creating mock objects, you need to define their behavior. This is done using `when()` and `thenReturn()` methods for simple cases, or `doAnswer()` for more complex scenarios.
- Simple Mocking: Use `when()` followed by `thenReturn()` to specify return values for methods.
java
when(database.isAvailable()).thenReturn(true);
- Complex Mocking: Use `doAnswer()` to handle complex logic, such as returning different values based on input parameters.
java
doAnswer(invocation -> {
// Custom logic here
Object[] args = invocation.getArguments();
// Handle args as needed
return "Mocked result";
}).when(database).isAvailable();
3. Mocking Complex Object Graphs**
For complex graphs, you'll need to mock each object and define how they interact with each other.
- Example: Suppose you have a `Service` class that depends on a `Database` and a `NetworkClient`. You would mock both `Database` and `NetworkClient`, then define their behaviors.
java
@Mock
private Database database;
@Mock
private NetworkClient networkClient;
@InjectMocks
private Service service;
// Define behaviors
when(database.isAvailable()).thenReturn(true);
when(networkClient.sendRequest(anyString())).thenReturn("Success");
4. Using Custom Answers**
Custom answers allow you to dynamically calculate return values based on method arguments. This is useful for mocking methods that should behave differently based on input.
- Example: Mocking a method that returns the first argument.
java
when(list.add(anyString())).thenAnswer(returnsFirstArg());
5. Testing the Complex Object Graph**
After setting up your mocks, you can test the behavior of your complex object graph by calling methods on the object under test and verifying that the expected interactions occur.
- Example: Testing if `service.query()` behaves correctly based on mock behaviors.
java
// Assuming service.query() uses database and networkClient
String result = service.query("some query");
assertEquals("Expected result", result);
// Verify interactions
verify(database).isAvailable();
verify(networkClient).sendRequest(anyString());
6. Best Practices**
- Keep Mocks Simple: Avoid overly complex mock logic. If mocks become too complicated, consider using a test double or a fake implementation instead.
- Use Argument Matchers: Use `any()`, `eq()`, etc., to match method arguments flexibly.
- Verify Interactions: Use `verify()` to ensure that the expected methods were called on mocks.
By following these steps and best practices, you can effectively use Mockito to mock complex object graphs in your unit tests.
Citations:[1] https://dzone.com/articles/mockito-custom-answers
[2] https://www.vogella.com/tutorials/Mockito/article.html
[3] https://semaphoreci.com/community/tutorials/stubbing-and-mocking-with-mockito-2-and-junit
[4] https://www.alibabacloud.com/blog/599923
[5] https://www.lambdatest.com/automation-testing-advisor/selenium/classes/org.mockito.invocation.MockHandler
[6] https://dzone.com/articles/mockito-basic-example-using-jdbc
[7] https://ioflood.com/blog/mockito-spy/
[8] https://stackoverflow.com/questions/15140467/mockito-stub-method-with-complex-object-as-a-parameter
[9] https://www.uwspace.uwaterloo.ca/bitstreams/d391872a-a430-41ea-a488-6c976a9e06c9/download