Google Analytics – исключение NullPointerException в тесте

Я получаю следующий NPE в тесте Robolectric на сервере сборки Travis, и у меня возникают проблемы с определением причины. Я не могу воспроизвести эту проблему локально.

Кто-нибудь знает, какие причины onServiceConnected будут вызваны? Это может помочь мне определить проблему. Из того, что я могу сказать, это проблема Google Play Services – Google Analytics.

java.lang.NullPointerException at com.google.android.gms.analytics.c$a.onServiceConnected(Unknown Source) at com.google.android.gms.analytics.c$a.onServiceConnected(Unknown Source) at org.robolectric.shadows.ShadowApplication$2.run(ShadowApplication.java:257) at org.robolectric.util.Scheduler$PostedRunnable.run(Scheduler.java:162) at org.robolectric.util.Scheduler.runOneTask(Scheduler.java:107) at org.robolectric.util.Scheduler.advanceTo(Scheduler.java:92) at org.robolectric.util.Scheduler.advanceToLastPostedRunnable(Scheduler.java:68) at org.robolectric.util.Scheduler.unPause(Scheduler.java:25) at org.robolectric.shadows.ShadowLooper.unPause(ShadowLooper.java:228) at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:267) at org.robolectric.util.ActivityController.create(ActivityController.java:144) at org.robolectric.util.ActivityController.create(ActivityController.java:154) at com.company.search.activities.loginjoin.LoginActivityTest.setup(LoginActivityTest.java:20) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:250) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355) at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) 

Насколько я знаю, я не инициализирую GA нигде для своих модульных тестов. У меня есть общий «фальшивый» отслежыватель аналитики, который используется при каждом тестировании. На всякий случай, я добавил следующую строку в конструкторе моего поддельного трекера, который создается в onCreate приложения:

 GoogleAnalytics.getInstance(context).setAppOptOut(true); 

В случае, если это имеет значение, оно разбивается на ящик с Java 1.7.0_u55.

РЕДАКТИРОВАТЬ:

Сегодня я видел следующее в журналах неудавшегося теста. Я не уверен, что это было причиной аварии. Можете дать некоторое представление.

 E/GAV3: Thread[GAThread,5,main]: Error on GAThread: java.lang.NullPointerException at org.robolectric.shadows.ShadowLooper.getMainLooper(ShadowLooper.java:66) at android.os.Looper.getMainLooper(Looper.java) at android.database.sqlite.SQLiteDatabase.isMainThread(SQLiteDatabase.java:391) at android.database.sqlite.SQLiteDatabase.getThreadDefaultConnectionFlags(SQLiteDatabase.java:381) at android.database.sqlite.SQLiteProgram.__constructor__(SQLiteProgram.java:58) at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:41) at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:31) at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:992) at android.database.DatabaseUtils.longForQuery(DatabaseUtils.java:799) at android.database.sqlite.SQLiteDatabase.getVersion(SQLiteDatabase.java:862) at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:242) at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164) at com.google.android.gms.analytics.ac$a.getWritableDatabase(Unknown Source) at com.google.android.gms.analytics.ac$a.getWritableDatabase(Unknown Source) at com.google.android.gms.analytics.ac.G(Unknown Source) at com.google.android.gms.analytics.ac.G(Unknown Source) at com.google.android.gms.analytics.ac.i(Unknown Source) at com.google.android.gms.analytics.ac.i(Unknown Source) at com.google.android.gms.analytics.s.bk(Unknown Source) at com.google.android.gms.analytics.s.bk(Unknown Source) at com.google.android.gms.analytics.s.bJ(Unknown Source) at com.google.android.gms.analytics.s.bJ(Unknown Source) at com.google.android.gms.analytics.sa(Unknown Source) at com.google.android.gms.analytics.s$2.run(Unknown Source) at com.google.android.gms.analytics.s$2.run(Unknown Source) at com.google.android.gms.analytics.t.run(Unknown Source) at com.google.android.gms.analytics.t.run(Unknown Source) E/GAV3: Thread[GAThread,5,main]: Google Analytics is shutting down. 

Похож на проблему с SQLite и GA?

Еще одно обновление:

Для получения обновлений см. Связанную проблему github ( https://github.com/robolectric/robolectric/issues/1075 ). На данный момент я возвращаюсь к v3.

Решение Akeem работает для меня.

Это то, что я использую для Robolectic 3.0, который имеет некоторые изменения:

 import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.shadows.ShadowApplication; ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application); shadowApplication.declareActionUnbindable("com.google.android.gms.analytics.service.START"); 

Исправление состоит в том, чтобы добавить следующие строки во ВСЕХ ваших наборов тестов (потому что вы не знаете, какой тестовый пример будет запущен первым)

 ShadowApplication shadowApplication = Robolectric.shadowOf(Robolectric.application); shadowApplication.declareActionUnbindable("com.google.android.gms.analytics.service.START"); 

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

 import android.app.Application; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowApplication; @Implements(Application.class) public class MyShadowApplication extends ShadowApplication { @Implementation public boolean bindService(Intent intent, final ServiceConnection serviceConnection, int i) { Log.d("Robolectric", intent.getAction()); return false; } } 

И запустите все ваши тестовые примеры с помощью теневого класса

 @Config(emulateSdk = 18, shadows = {MyShadowApplication.class}) 

Это то, что я выяснил (и решил в моем случае): Где-то (возможно, при инициализации Google Analytics) есть вызов для запуска службы.

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

 Robolectric.getShadowApplication().setComponentNameAndServiceForBindService( new ComponentName(Robolectric.application, MyService.class), new StalkerService() { @Override public Context getApplicationContext() { return Robolectric.application; } }.onBind(null) ); 

В моем коде TestApplication, перед вызовом super.onCreate . К счастью, это решило мою проблему – не больше NPE, и все тесты проходят.

Но потом я заметил, что это очень странное решение, так как я предоставляю Robolectric связующим для моей службы, а не службы GA … Но это работает, и я не знаю, почему. Итак, я собираюсь оставить его здесь, чтобы вы могли проверить, исправляет ли он ваши проблемы.