Mocking System Services for Unit Tests
Testing AOSP framework components is notoriously difficult because they often rely on a deep web of system services (e.g., ActivityManagerService, PowerManagerService). Mockito allows you to isolate the class under test by replacing these heavy dependencies with controllable mock objects.
In AOSP, you typically include Mockito in your Android.bp test module using the mockito-target-extended-minus-junit4 static library.
Mock, Spy, ArgumentCaptor
Mockito provides three primary tools for manipulating objects in tests:
- Mock: Creates a completely empty shell of a class or interface. By default, all methods return null, zero, or false. You explicitly define the behavior of the methods you care about.
- Spy: Wraps a real object. Method calls pass through to the real implementation unless you explicitly mock them. This is useful for partial mocking.
- ArgumentCaptor: Allows you to intercept and inspect the exact parameters passed to a mocked method.
import static org.mockito.Mockito.*;
public class BatteryMonitorTest {
@Test
public void testBatteryLowAlert() {
// Create a mock Intent
Intent mockIntent = mock(Intent.class);
when(mockIntent.getAction()).thenReturn(Intent.ACTION_BATTERY_LOW);
// Spy on the object under test to verify internal method calls
BatteryMonitor monitor = spy(new BatteryMonitor());
monitor.onReceive(mockContext, mockIntent);
// Verify that the alert method was called
verify(monitor).triggerLowBatteryAlert();
}
}
Testing Binder Services with Mockito
Many AOSP components communicate via Binder IPC. Mocking Binder interfaces directly can be complex due to the native layer involvement.
Instead of mocking the native Binder proxy, the standard practice is to mock the Java interface generated from the .aidl file.
// Mocking an AIDL generated interface
IPackageManager mockPackageManager = mock(IPackageManager.class);
// Stubbing a method that throws a RemoteException
when(mockPackageManager.getPackageUid("com.example.app", 0, 0))
.thenReturn(10050);
By injecting this mockPackageManager into your framework class, you can test how your code handles various package states without needing a running system server.
ExtendedMockito for Static Mocking
Standard Mockito cannot mock static methods, final classes, or constructors. This is a massive hurdle in AOSP, where utility classes like SystemProperties or Settings.Global are ubiquitous.
To solve this, AOSP leverages ExtendedMockito (built on top of dexmaker-mockito-inline).
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import org.mockito.MockitoSession;
public class SystemPropTest {
private MockitoSession mSession;
@Before
public void setUp() {
// Initialize static mocking for android.os.SystemProperties
mSession = ExtendedMockito.mockitoSession()
.mockStatic(SystemProperties.class)
.startMocking();
}
@After
public void tearDown() {
mSession.finishMocking();
}
@Test
public void testFeatureFlag() {
// Stub the static method
ExtendedMockito.doReturn("true")
.when(() -> SystemProperties.get("ro.feature.enabled", "false"));
// Now test your code that reads this property
assertTrue(FeatureManager.isEnabled());
}
}
Note: Static mocking introduces performance overhead and potential test pollution. Always ensure the MockitoSession is properly closed in the @After teardown method.