How to test class using content resolver/provider?

孤人 提交于 2021-01-20 16:43:21

问题


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()) {
                //...
            }
        //..
    }
}

回答1:


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.




回答2:


Here is an example test that returns mock data from a content provider using getContentResolver().query.

It should work for any content provider, with a few modifications, but this example mocks returning phone numbers from the Contacts content provider

Here are the general steps:

  1. Creates appropriate cursor using MatrixCursor
  2. Extend MockContentProvider to return the created cursor
  3. Add the provider to a MockContentResolver using the addProvider and setContentResolver
  4. Add the MockContentResolver to an extended MockContext
  5. Passes the context into the the class under test

Because query is a final method, you need to mock not only MockContentProvider but also MockContentResolver. Otherwise you will get an error when acquireProvider is called during the query method.

Here is the example code:

public class MockContentProviderTest extends AndroidTestCase{
    public void testMockPhoneNumbersFromContacts(){
        //Step 1: Create data you want to return and put it into a matrix cursor
        //In this case I am mocking getting phone numbers from Contacts Provider
        String[] exampleData = {"(979) 267-8509"}; 
        String[] examleProjection = new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER};
        MatrixCursor matrixCursor = new MatrixCursor(examleProjection);
        matrixCursor.addRow(exampleData);

        //Step 2: Create a stub content provider and add the matrix cursor as the expected result of the query
        HashMapMockContentProvider mockProvider = new HashMapMockContentProvider();
        mockProvider.addQueryResult(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, matrixCursor);

        //Step 3: Create a mock resolver and add the content provider.
        MockContentResolver mockResolver = new MockContentResolver();
        mockResolver.addProvider(ContactsContract.AUTHORITY /*Needs to be the same as the authority of the provider you are mocking */, mockProvider);

        //Step 4: Add the mock resolver to the mock context
        ContextWithMockContentResolver mockContext = new ContextWithMockContentResolver(super.getContext());
        mockContext.setContentResolver(mockResolver);

        //Example Test 
        ExampleClassUnderTest underTest = new ExampleClassUnderTest();
        String result = underTest.getPhoneNumbers(mockContext);
        assertEquals("(979) 267-8509",result);
    }

    //Specialized Mock Content provider for step 2.  Uses a hashmap to return data dependent on the uri in the query
     public class HashMapMockContentProvider extends MockContentProvider{
         private HashMap<Uri, Cursor> expectedResults = new HashMap<Uri, Cursor>();
         public void addQueryResult(Uri uriIn, Cursor expectedResult){
             expectedResults.put(uriIn, expectedResult);
         }
         @Override
         public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){
                return expectedResults.get(uri);
         } 
     }

     public class ContextWithMockContentResolver extends RenamingDelegatingContext {
            private ContentResolver contentResolver;
            public void setContentResolver(ContentResolver contentResolver){ this.contentResolver = contentResolver;}
            public ContextWithMockContentResolver(Context targetContext) { super(targetContext, "test");}
            @Override public ContentResolver getContentResolver() { return contentResolver; }
            @Override public Context getApplicationContext(){ return this; } //Added in-case my class called getApplicationContext() 
     }

     //An example class under test which queries the populated cursor to get the expected phone number 
     public class ExampleClassUnderTest{
         public  String getPhoneNumbers(Context context){//Query for  phone numbers from contacts
                String[] projection = new String[]{ ContactsContract.CommonDataKinds.Phone.NUMBER};
                Cursor cursor= context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
                cursor.moveToNext();
                return cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
         }
     }
}

If you don't want to pass context in:

If you wanted to have it returned by getContext() in the class under test instead of passing it in you should be able to override getContext() in your android test like this

@Override
 public Context getContext(){
    return new ContextWithMockContentResolver(super.getContext());   
 } 



回答3:


After reading docs I was able to write MockContentProvider that implemented return of appropriate cursors. Then I added this provider to MockContentResolver using addProvider.




回答4:


I haven't used Mockito yet, but for content providers, you can rely on Robolectric. https://github.com/juanmendez/jm_android_dev/blob/master/16.observers/00.magazineAppWithRx/app/src/test/java/ContentProviderTest.java



来源:https://stackoverflow.com/questions/6610437/how-to-test-class-using-content-resolver-provider

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!