Запрос с использованием MockContentResolver приводит к NullPointerException

У нас есть тестовый класс JUnit, который расширяет ActivityInstrumentationTestCase2<CommentActivity>. Тест (и класс, который мы тестируем) использует CommentContentProvider, который расширяет ContentProvider, для доступа к базе данных SQLite, и мы получаем NullPointerException [полная трассировка стека ниже] при выполнении запроса к провайдеру.

Мы создаем экземпляр MockContentResolver, как показано ниже:

MockContentResolver mResolver;

public void setUp() {
    super.setUp();
    CommentContentProvider ccp = new CommentContentProvider();
    mResolver = new MockContentResolver();
    mResolver.addProvider(CommentContentProvider.AUTHORITY, ccp);
}

Позже, в наших тестах, при вызове следующего кода мы получаем NullPointerException:

Cursor mCursor = mResolver.query(Uri.parse(mUri), null, null, null, null);

Мы получим тот же результат, даже если подождем создания экземпляра MockContentResolver, пока у нас не будет копии тестируемой активности:

mActivity = getActivity();
MockContentResolver mResolver = new MockContentResolver(mActivity);

Мы проверили, что mActivity не равно нулю.

Коллега изучил исходный код Android (не установленный в нашей системе) и обнаружил, что непосредственная причина ошибки заключается в том, что getContext() возвращает null в первая строка ContentProvider.enforceReadPermissionInner ().

Мы рассмотрели этот вопрос, который изначально казался похожим, но я думаю, была совсем другая проблема. Этот вопрос также является похожим симптомом проблемы, но они не создать экземпляр их MockContentResolver. У нас проблемы с созданием экземпляров.

Вот трассировка стека, которую мы получаем:

java.lang.NullPointerException
at android.content.ContentProvider$Transport.enforceReadPermissionInner(ContentProvider.java:449)
at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:394)
at android.content.ContentProvider$Transport.query(ContentProvider.java:194)
at android.content.ContentResolver.query(ContentResolver.java:461)
at android.content.ContentResolver.query(ContentResolver.java:404)
at packagename.test.FooActivityTest.getNumCommentsForRecipient(FooActivityTest.java:84)
at packagename.test.FooActivityTest.testCommentEntryInternal(FooActivityTest.java:91)
at packagename.test.FooActivityTest.testCommentEntry1(FooActivityTest.java:108)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.access$000(InstrumentationTestCase.java:36)
at android.test.InstrumentationTestCase$2.run(InstrumentationTestCase.java:189)
at android.app.Instrumentation$SyncRunnable.run(Instrumentation.java:1719)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4998)
at java.lang.reflect.Method.invokeNative(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
at dalvik.system.NativeStart.main(Native Method)

Как мы можем решить эту проблему?




Ответы (1)


У меня была аналогичная проблема при тестировании поставщика контента, который внутренне полагался на другого поставщика контента для записи некоторых метаданных.

Прежде всего, вам может быть лучше использовать класс ProviderTestCase2, который сделает большую часть работу по настройке тестируемого провайдера для вас. Возможно, это значительно облегчит вам жизнь. (Для меня этого было недостаточно, потому что это поможет вам только с одним провайдером, мне нужно было два.)

Если это невозможно для вас, вот что помогло мне:

Ваш запрос завершается ошибкой, потому что у вашего провайдера никогда не было прикрепленного к нему контекста. Вы должны сделать это самостоятельно, вручную, о чем документация забывает упомянуть. Сделай это:

public void setUp() {
    super.setUp();
    CommentContentProvider ccp = new CommentContentProvider();

    // Add this line to attach context:
    ccp.attachInfo(mActivity, null);

    mResolver = new MockContentResolver();
    mResolver.addProvider(CommentContentProvider.AUTHORITY, ccp);
}

Я не уверен на 100%, какой контекст прикрепить, чтобы ваш тест был изолирован от остального мира, ProviderTestCase2 создает целую цепочку фиктивных контекстов. Если у вас возникли проблемы, просмотрите RenamingDelegatingContext и IsolatedContext, это те, которые использует ContentProviderTestCase2. (Посмотрите на его метод setUp()).

Надеюсь, это поможет вам!

person BadIdeaException    schedule 18.03.2014
comment
Спасибо Спасибо спасибо! Это имеет смысл. Присоединение mActivity устранило исключение NullPointerException, хотя мне все еще нужно повозиться с ним, чтобы заставить его использовать тот же контекст, что и тестируемый класс. - person Ellen Spertus; 18.03.2014
comment
Рад, что это помогло вам. Я знаю, что это сводило меня с ума некоторое время, пока я не понял это. Пожалуйста, рассмотрите возможность принятия ответа, чтобы я мог получить баллы. :) - person BadIdeaException; 18.03.2014
comment
Я не оригинальный постер; Я ее учитель (что дает вам представление о том, как я расстроился из-за того, что не смог решить поставленную задачу). Я посоветую ей не отмечать вопрос как ответ, пока мы не решим его полностью, но я сильно подозреваю, что некоторые заслуженные баллы ждут вас в будущем. :-) - person Ellen Spertus; 19.03.2014
comment
Спасибо, это имеет большой смысл и, кажется, решило проблему! - person ctaymor; 19.03.2014
comment
В итоге нам потребовалось использовать RenamingDelegatingContext. Возможно, это не лучшее решение, но нам также нужно было использовать MockContentResolver в самой нашей FooActivity для целей тестирования. Мы настроили его так, чтобы FooActivityTest передал дополнение в намерении, которое было флагом DEBUG, а FooActivity проверил наличие флага отладки и использовал getActivity() для получения контекста, если он был ложным, и создал MockContentResolver и сделал весь шебанг с созданием RenamingDelegatingContext и т. д. в FooActivity, чтобы кнопка сохранения сохранялась в нашей тестовой базе данных, а не в рабочей/действующей базе данных. - person ctaymor; 19.03.2014
comment
MockContentResolver и RenamingDelegatingContext предназначены именно для таких вещей, так что не беспокойтесь о том, что это не очень хорошее решение. ;) - person BadIdeaException; 19.03.2014