10

コンテンツ リゾルバーをクエリするクラスをテストしようとしています。

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

5 に答える 5

18

以下は、getContentResolver().query を使用してコンテンツ プロバイダーからモック データを返すテストの例です。

いくつかの変更を加えると、どのコンテンツ プロバイダーでも機能するはずですが、この例では、Contacts コンテンツ プロバイダーから返される電話番号を模擬しています。

一般的な手順は次のとおりです。

  1. MatrixCursor を使用して適切なカーソルを作成します
  2. MockContentProvider を拡張して作成したカーソルを返す
  3. addProvider と setContentResolver を使用してプロバイダーを MockContentResolver に追加する
  4. MockContentResolver を拡張 MockContext に追加する
  5. テスト対象のクラスにコンテキストを渡します

query は final メソッドなので、MockContentProvider だけでなく MockContentResolver もモックする必要があります。そうしないと、query メソッドで 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());   
 } 
于 2014-01-24T23:44:45.693 に答える
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;
}
}

Jenn Weingarten の回答を使用して書かれています。注意すべき点: MockContentProvider-パブリックである必要があります -テスト対象のクラスではなくContextfrom メソッドを使用する必要があります。そうしないと、モック データではなくデバイスからの実際のデータ (この場合は連絡先) にアクセスします -テストを実行してはなりませんAndroidJUnit4 ランナー -もちろん、テストは android 計測テストとして実行する必要があります -テストのコンストラクターの 2 番目のパラメーター (権限) は、テスト対象のクラスでクエリされた URI と同じでなければなりません -モック プロバイダーのタイプは、クラス パラメーターとして提供する必要がありますthis.getMockContext()this.getContext()

基本的に ProviderTestCase2 は、モック コンテキスト、モック コンテンツ リゾルバー、およびモック コンテンツ プロバイダーの初期化を行います。

Android APIに大きく依存しているクラスのmockitoとjunit4を使用してローカルユニットテストを作成しようとする代わりに、古いテスト方法を使用する方がはるかに簡単であることがわかりました。

于 2016-02-28T09:06:22.510 に答える
2

ドキュメントを読んだ後MockContentProvider、適切なカーソルの戻りを実装するように書くことができました。次に、このプロバイダーをMockContentResolverusingに追加しましaddProviderた。

于 2011-07-10T17:29:19.813 に答える
0

私はまだ Mockito を使用していませんが、コンテンツ プロバイダーには Robolectric を利用できます。https://github.com/juanmendez/jm_android_dev/blob/master/16.observers/00.magazineAppWithRx/app/src/test/java/ContentProviderTest.java

于 2016-04-09T05:16:50.117 に答える