How to test class using content resolver/provider?

前端 未结 4 1904
一向
一向 2021-02-04 12:44

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

I would like to use MockContentResolver and mock query method.

The problem is

4条回答
  •  余生分开走
    2021-02-04 13:11

    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 expectedResults = new HashMap();
             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());   
     } 
    

提交回复
热议问题