如何使用内容解析器/提供程序测试类?

pix*_*xel 9 android unit-testing final android-contentresolver

我正在尝试测试查询内容解析器的类.

我想用MockContentResolver和模拟query方法.

问题是这种方法是最终的.我该怎么办?使用模拟框架?模拟其他课程?提前致谢.

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()) {
                //...
            }
        //..
    }
}
Run Code Online (Sandbox Code Playgroud)

Jen*_*ten 18

下面是一个示例测试,它使用getContentResolver().query从内容提供程序返回模拟数据.

它应该适用于任何内容提供商,只需进行一些修改,但此示例模拟从联系人内容提供商返回电话号码

以下是一般步骤:

  1. 使用MatrixCursor创建适当的游标
  2. 扩展MockContentProvider以返回创建的游标
  3. 使用addProvider和setContentResolver将提供程序添加到MockContentResolver
  4. 将MockContentResolver添加到扩展的MockContext
  5. 将上下文传递到被测试的类中

因为查询是最终方法,所以您不仅需要模拟MockContentProvider,还需要模拟MockContentResolver.否则,在查询方法期间调用acquireProvider时,您将收到错误.

这是示例代码:

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));
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

如果您不想传递上下文:

如果你想让getContext()在被测试的类中返回而不是传入它,你应该能够在你的android测试中覆盖getContext(),就像这样

@Override
 public Context getContext(){
    return new ContextWithMockContentResolver(super.getContext());   
 } 
Run Code Online (Sandbox Code Playgroud)


Kam*_*ski 5

这个问题已经很老了,但人们可能仍然像我一样面临这个问题,因为没有很多关于测试的文档。

对我来说,对于依赖于内容提供者(来自 android API)的测试类,我使用了 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;
}
}
Run Code Online (Sandbox Code Playgroud)

它是使用 Jenn Weingarten 的答案编写的。需要注意的几件事:MockContentProvider- 你必须是公开的 - 你必须使用Contextfrom 方法this.getMockContext()而不是this.getContext()在你的被测类中,否则你将访问的不是模拟数据而是来自设备的真实数据(在这种情况下 - 联系人) - 测试不能与AndroidJUnit4 运行程序 - 测试当然必须作为 android 插装测试运行 - 测试(权限)的构造函数中的第二个参数必须与被测类中查询的 URI 相同 - 模拟提供程序的类型必须作为类参数提供

基本上 ProviderTestCase2 使您可以初始化模拟上下文、模拟内容解析器和模拟内容提供者。

我发现使用旧的测试方法更容易,而不是尝试使用 mockito 和 junit4 为高度依赖于 android api 的类编写本地单元测试。