我正在尝试测试查询内容解析器的类。
我想使用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()) {
//...
}
//..
}
}
答案 0 :(得分:18)
以下是使用getContentResolver()。query从内容提供程序返回模拟数据的示例测试。
它适用于任何内容提供商,只需进行一些修改,但此示例模拟从联系人内容提供商返回电话号码
因为查询是最终方法,所以您不仅需要模拟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));
}
}
}
如果你想让被测试的类中的getContext()返回而不是传入它,你应该能够在你的android测试中覆盖getContext(),就像这样
@Override
public Context getContext(){
return new ContextWithMockContentResolver(super.getContext());
}
答案 1 :(得分:4)
这个问题很老,但人们可能仍然会像我一样面对这个问题,因为没有很多关于测试这个问题的文档。
对我来说,对于依赖于内容提供程序的测试类(来自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;
}
}
使用Jenn Weingarten的回答写的。
几点注意事项:
你的MockContentProvider
必须是公开的
- 您必须在测试的类中使用方法Context
而不是this.getMockContext()
中的this.getContext()
,否则您将访问不是模拟数据,而是来自设备的实际数据(在这种情况下 - 联系人)
-Test不得与AndroidJUnit4跑步者一起运行
-Test当然必须作为android仪器测试运行
- 与测试中的类查询的URI相比,测试(权限)的构造函数中的第二个参数必须相同
- 必须提供模拟提供程序的类型作为类参数
基本上,ProviderTestCase2可以帮助您初始化模拟上下文,模拟内容解析器和模拟内容提供程序。
我发现使用旧的测试方法更容易,而不是尝试使用mockito和junit4编写本地单元测试,因为它高度依赖于android api。
答案 2 :(得分:2)
阅读完文档后,我能够编写MockContentProvider
来实现适当游标的返回。然后,我使用MockContentResolver
将此提供商添加到addProvider
。
答案 3 :(得分:0)
我还没有使用Mockito,但对于内容提供商,你可以依靠Robolectric。 https://github.com/juanmendez/jm_android_dev/blob/master/16.observers/00.magazineAppWithRx/app/src/test/java/ContentProviderTest.java