Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test class using content resolver/provider?

I'm trying to test class that queries content resolver.

I would like to use MockContentResolver and mock query method.

The problem is that this method is final. What should I do? Use mocking framework? Mock other class? Thanks in advance.

public class CustomClass {

    private ContentResolver mContentResolver;

    public CustomClass(ContentResolver contentResolver) {
        mContentResolver = contentResolver;
    }

    public String getConfig(String key) throws NoSuchFieldException {
        String value = null;

            Cursor cursor = getContentResolver().query(...);
            if (cursor.moveToFirst()) {
                //...
            }
        //..
    }
}
like image 784
pixel Avatar asked Jul 07 '11 12:07

pixel


2 Answers

Here is an example about how to stub a ContentResolver with mockk Library and Kotlin.

NOTE: this test seems that is not working if you run this in an emulator, fails in an emulator with API 23 with this error "java.lang.ClassCastException: android.database.MatrixCursor cannot be cast to java.lang.Boolean".

Clarified that, lets do this. Having an extension from Context object, that is called, val Context.googleCalendars: List<Pair<Long, String>>, this extension filters calendars witch calendar name doesn't ends with "@google.com", I am testing the correct behavior of this extension with this AndroidTest.

Yes you can download the repo from here.

@Test
    fun getGoogleCalendarsTest() {

        // mocking the context
        val mockedContext: Context = mockk(relaxed = true)

        // mocking the content resolver
        val mockedContentResolver: ContentResolver = mockk(relaxed = true)

        val columns: Array<String> = arrayOf(
            CalendarContract.Calendars._ID,
            CalendarContract.Calendars.NAME
        )

        // response to be stubbed, this will help to stub
        // a response from a query in the mocked ContentResolver
        val matrixCursor: Cursor = MatrixCursor(columns).apply {

            this.addRow(arrayOf(1, "[email protected]"))
            this.addRow(arrayOf(2, "name02")) // this row must be filtered by the extension.
            this.addRow(arrayOf(3, "[email protected]"))
        }

        // stubbing content resolver in the mocked context.
        every { mockedContext.contentResolver } returns mockedContentResolver

        // stubbing the query.
        every { mockedContentResolver.query(CalendarContract.Calendars.CONTENT_URI, any(), any(), any(), any()) } returns matrixCursor

        val result: List<Pair<Long, String>> = mockedContext.googleCalendars

        // since googleCalendars extension returns only the calendar name that ends with @gmail.com
        // one row is filtered from the mocked response of the content resolver.
        assertThat(result.isNotEmpty(), Matchers.`is`(true))
        assertThat(result.size, Matchers.`is`(2))
    }
like image 142
Akhha8 Avatar answered Nov 16 '22 01:11

Akhha8


This question is pretty old but people might still face the issue like me, because there is not a lot of documentation on testing this.

For me, for testing class which was dependent on content provider (from android API) I used ProviderTestCase2

public class ContactsUtilityTest extends ProviderTestCase2<OneQueryMockContentProvider> {


private ContactsUtility contactsUtility;

public ContactsUtilityTest() {
    super(OneQueryMockContentProvider.class, ContactsContract.AUTHORITY);
}


@Override
protected void setUp() throws Exception {
    super.setUp();
    this.contactsUtility = new ContactsUtility(this.getMockContext());
}

public void testsmt() {
    String phoneNumber = "777777777";

    String[] exampleData = {phoneNumber};
    String[] examleProjection = new String[]{ContactsContract.PhoneLookup.NUMBER};
    MatrixCursor matrixCursor = new MatrixCursor(examleProjection);
    matrixCursor.addRow(exampleData);

    this.getProvider().addQueryResult(matrixCursor);

    boolean result = this.contactsUtility.contactBookContainsContact(phoneNumber);
    // internally class under test use this.context.getContentResolver().query(); URI is ContactsContract.PhoneLookup.CONTENT_FILTER_URI
    assertTrue(result);
}


}


public class OneQueryMockContentProvider extends MockContentProvider {
private Cursor queryResult;

public void addQueryResult(Cursor expectedResult) {
    this.queryResult = expectedResult;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    return this.queryResult;
}
}

It's written by using Jenn Weingarten's answer. Few things to note: -your MockContentProvider must be public -you must use Context from method this.getMockContext() instead of this.getContext() in your class under test, otherwise you will access not mock data but real data from device (in this case - contacts) -Test must not be run with AndroidJUnit4 runner -Test of course must be run as android instrumented test -Second parameter in constructor of the test (authority) must be same compared to URI queried in class under test -Type of mock provider must be provided as class parameter

Basically ProviderTestCase2 makes for you initializing mock context, mock content resolver and mock content provider.

I found it much more easier to use older method of testing instead of trying to write local unit test with mockito and junit4 for class which is highly dependent on android api.

like image 20
Kamil Orzechowski Avatar answered Nov 16 '22 01:11

Kamil Orzechowski