Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Analytics - NullPointerException in Test

I'm getting the following NPE in a Robolectric test on a Travis build-server, and I'm having trouble pinpointing why. I'm unable to reproduce this issue locally.

Anybody know what causes onServiceConnected to be called? That might help me pinpoint the issue. From what I can tell this is a Google Play Services - Google Analytics issue.

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)

To my knowledge, I'm not initializing GA anywhere for my unit tests. I have a general "fake" analytics tracker that gets used whenever tests are run. Just in case, I've added the following line in the constructor of my fake tracker which gets created in onCreate of the application:

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

In case this is of relevance, it's breaking on a box with Java 1.7.0_u55.

EDIT:

I saw the following in the logs of a failed test today. I'm not sure it was the cause of the crash though. Might give some insight.

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.s.a(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.

Looks like a problem with SQLite and GA?

Another Update:

For updates, see the linked github issue (https://github.com/robolectric/robolectric/issues/1075). I'm reverting back to v3 for the time being.

like image 687
loeschg Avatar asked May 07 '14 20:05

loeschg


3 Answers

The solution of Akeem works for me.

This is what I use for Robolectic 3.0, which has some changes:

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");
like image 157
jiahao Avatar answered Oct 07 '22 01:10

jiahao


The fix is to add the following lines in ALL of your test suites (because you don't know which test case would be run first)

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

Alternatively you can create a custom shadow class for your application that doesn't bind services:

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;
    }
}

And run all your test cases with the shadow class

@Config(emulateSdk = 18, shadows = {MyShadowApplication.class})
like image 33
Akeem Avatar answered Oct 07 '22 00:10

Akeem


This is what I figured out (and solved in my case) : Somewhere (probably when initializing Google Analytics), there is a call to start a service.

I thought that this call was to start one of my app's services, so I added

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

in my TestApplication code, before calling super.onCreate. Gladly, it solved my problem - no more NPE, and all tests pass.

But, then I noticed that this is very weird solution, since I'm providing Robolectric the binder for my Service and not GA service... But it works, and I'm not sure why. So, I'm going to leave it here for you to test if it fixes your issues too.

like image 1
Menny Avatar answered Oct 07 '22 00:10

Menny