Module 2: Mocking 2
Module Overview
Explore advanced mocking techniques and best practices to improve your testing workflow and increase code reliability.
Advanced Mocking Techniques
Once you understand the basics of mocking, you can leverage more sophisticated techniques to handle complex testing scenarios. Mockito provides powerful capabilities for defining precise behavior, verifying interactions, and capturing arguments.
This module builds on the foundation of basic mocking to help you create comprehensive test suites for real-world applications.
// Example of verifying method calls
@Test
public void registerRenter_eligibleDriver_successfullyRegisters() throws NotEligibleToRegisterException {
// GIVEN
String driverLicenseId = "1234ABC567";
Driver driver = new Driver("Danica Patrick", 38, driverLicenseId);
when(driverLicenseService.isValid(driverLicenseId)).thenReturn(true);
// WHEN
renterRegistrar.registerRenter(driver);
// THEN
verify(renterDao).addRenter(driver);
}
Learning Objectives
- Master advanced mockito techniques for complex scenarios
- Learn to mock specific behaviors and exceptions
- Understand how to verify interaction order and number of invocations
- Practice using argument captors for complex assertions
- Implement advanced mocking strategies for production-level testing
- Design and implement tests that verify dependencies interact as expected
- Create comprehensive tests for exception handling from dependencies
- Identify and debug issues using log files and fragments
- Master testing of void methods that perform actions rather than return values
- Understand what not to mock and best practices for effective mocking
Advanced Verification Techniques
Beyond basic verification, Mockito offers several modes to verify complex interactions:
- Verifying call counts: Ensure methods are called exactly the expected number of times
- Capturing arguments: Examine the actual values passed to mocked methods
- Verifying order: Confirm that method calls happen in the expected sequence
// Verifying number of calls
verify(renterDao, times(1)).addRenter(driver);
// Verification modes
verify(renterDao, atLeastOnce()).addRenter(driver);
verify(renterDao, atMost(3)).getRegisteredRenter(anyString());
verify(renterDao, never()).removeRenter(anyString());
// Using ArgumentCaptor
ArgumentCaptor<Driver> driverCaptor = ArgumentCaptor.forClass(Driver.class);
verify(renterDao).addRenter(driverCaptor.capture());
Driver capturedDriver = driverCaptor.getValue();
assertEquals("Danica Patrick", capturedDriver.getName());
Mastery Task 7: We Will Mock You
Mastery Task Guidelines
Mastery Tasks are opportunities to test your knowledge and understanding through code. When a mastery task is shown in a module, it means that we've covered all the concepts that you need to complete that task. You will want to make sure you finish them by the end of the week to stay on track and complete the unit.
Each mastery task must pass 100% of the automated tests and code styling checks to pass each unit. Your code must be your own. If you have any questions, feel free to reach out for support.
The good news: we just hired a senior engineer for the team. The bad news: she is outraged that we do not use mocks in our ShipmentService tests! She explains that without mocks, our unit tests are brittle because they depend on the contents of the actual PackagingDatastore. This means we are dependent on actual data, that could be changed at any time. If it changes then our unit tests could fail, and we can't deploy new code!
Granted, all our project code is locally available Java classes; but this problem becomes even worse if we are depending on an external class for our unit tests to pass. Let's fix this problem by updating our ShipmentServiceTest class to use mocks and avoid calling the actual PackagingDao. Since the PackagingDao depends on the PackagingDataStore we will have removed our use of actual data in our ShipmentService tests by mocking the PackagingDao.
You can use PrepareShipmentActivityTest as a reference since its tests already use mocks.
Feel free to mock dependencies in any other tests, too; mocks are really neat, and we're not going to stop you from getting more experience if you like.
Exit Checklist
./gradlew -q clean :test --tests 'tct.MT7*'
passes- All TCTs should now pass -
./gradlew -q clean :test --tests 'tct*'
passes ./gradlew -q clean :test --tests 'com.amazon.ata.*'
passes- You're ShipmentServiceTest no longer depends on the actual implementation of PackagingDAO