Content Providers are one of the four primary components of an Android application. If you’re an Android developer, you’re probably more familiar with the other three: Activity, Service, Broadcast Receiver). Content Providers are used for sharing data between applications. If you’re querying information about the user’s contacts, that comes from the Contacts Provider. If you’re picking an image from the user’s gallery, you’re getting a Content URI back from a separate Content Provider.
A Content Resolver is used to resolve that Content URI into useful information: a cursor containing contact information or an input stream representing a gallery image, e.g., via contentResolver.query(Uri) or contentResolver.openInputStream(Uri).
When testing an Android application, it can be helpful to use a mock Content Resolver instead of the real thing — e.g., to simulate contacts or gallery images on the device without actually adding them. This is doubly useful when testing on an emulator, which does not come preloaded with any contacts or images.
This is quite challenging for a few reasons (and also assumes that you are familiar with Dagger2 and Mockito):
Content Resolvers are acquired via context.getContentResolver(). This means that in order for your activity/service/broadcast receiver to use a mock, you have to have dependency injection set up. In addition, you can never call getContentResolver(), because this will always return the system’s actual Content Resolver.
Content Resolver methods are final, which means they cannot be mocked. Instead, we have to use a mock content resolver, which requires a bit of additional setup. (Content Resolvers are responsible for routing calls between the various Content Providers on the system, and so a mock Content Resolver needs to define a routing map of its own.)
You might also be using CursorLoader to perform queries asynchronously and to observe content URIs. Behind the scenes, CursorLoader calls context.getContentResolver(), which, as explained above, bypasses any mock that you’ve created.
With that disclaimer out of the way, here’s how create and use a mock content resolver for testing:
Write a Dagger Content Provider module. In production, the actual Content Resolver is used, but this provides us the flexibility to inject a mock under test.
@Module
public final class ContentProviderModule {
@Provides
ContentResolver provideContentResolver(Context context) {
return context.getContentResolver();
}
}
For the test, write a mock Content Resolver. There are a few tricks involved here.
First, a mock Content Provider must be written. (Remember, a Content Resolver is responsible for routing between one or more Content Providers. So in order to use mock Content Resolver, we must also have some mock Content Providers).
Second, Content Providers have some internal workings (such as the Transport class used for IPC) which still need to work with our mock. So we can either subclass Content Provider for each test (which is annoying), or we can use Mockito and a Delegating wrapper to ensure that the transport mechanisms are intact (which is better).
public class MockContentProviderWrapper extends ContentProvider {
private final ContentProvider mMockProvider;
public MockContentProviderWrapper(ContentProvider mock, String authority, Context context) {
mMockProvider = mock;
ProviderInfo info = new ProviderInfo();
info.authority = authority;
attachInfo(context, info);
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return mMockProvider.query(uri, projection, selection, selectionArgs, sortOrder);
}
}
Now we can use Mockito and inject a mock Content Resolver.
@Module
class MockContentProviderModule {
@Singleton
@Provides
ContentResolver provideContentResolver(ContentProvider provider, Context context) {
MockContentResolver mockResolver = new MockContentResolver();
MockContentProviderWrapper wrapper = new MockContentProviderWrapper(provider,
MyContract.CONTENT_AUTHORITY, context);
mockResolver.addProvider(MyContract.CONTENT_AUTHORITY, wrapper);
return mockResolver;
}
@Singleton
@Provides
ContentProvider provideMyProvider() {
return mock(ContentProvider.class);
}
And your test might look like this:
@Test
public void ensureEmptyView_visibleWhenNoData() {
when(mMockProvider.query(…))
.thenReturn(new MatrixCursor(new String[0]));
mTestRule.launchActivity(new Intent());
onView(withId(R.id.my_empty_view)).check(ViewAssertions.matches(
withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)));
}
If you’re using CursorLoader, you need to take an additional step. Because CursorLoader calls context.getContentResolver() and we can’t easily modify that class, we need to use a Context Wrapper whose getContentResolver() method returns our mock. This is left as an exercise for the reader.
Phew! That is a ton of work just to be able to write tests. We recommend this technique only if you have to interact with system-level Content Providers (such as the Contacts Provider). If you’re writing your own Content Providers and don’t have to share data with other apps, we recommend taking a look at the new Android architecture components, like Room.