NPE when creating activity based on MapActivity #972

Closed
ryazmin opened this Issue Feb 18, 2014 · 8 comments

4 participants

@ryazmin

Activity is very simple without map view in layout, it's just extends com.google.android.maps.MapActivity

the test code

@Test
public void launchActivity() throws Exception {
    Robolectric.buildActivity(SimpleActivity.class).create().visible().start().resume().get();
}

Issue looks similar to #918
and similar question on SO: http://stackoverflow.com/questions/21791942/testing-my-mapactivity-with-robolectric

Exception stack trace:
java.lang.NullPointerException
at android.app.Activity.attach(Activity.java:4967)
at org.fest.reflect.method.Invoker.invoke(Invoker.java:112)
at org.robolectric.util.ActivityController.attach(ActivityController.java:90)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:114)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:256)
at org.robolectric.util.ActivityController.create(ActivityController.java:111)
at org.robolectric.util.ActivityController.create(ActivityController.java:123)
at com.mytests.activity.SimpleActivityTest.launchActivity(SimpleActivityTest.java:37)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:234)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:175)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

@ryazmin

any workaround welcome

@SuperJugy

Have you tried debugging to know what is null in the activity oncreate?

@ryazmin

I tried to debug and as I can see the member mFragments of android.app.Activity is null for some reason.
Definition of this member:
final FragmentManagerImpl mFragments = new FragmentManagerImpl();

Here is the source of 'attach' method and crash happens right after attachBaseContext returns.

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config) {
    attachBaseContext(context);

    mFragments.attachActivity(this, mContainer, null);

.....

activity instance created by ActivityController:
public ActivityController(Class activityClass) {
this.activity = constructor().in(activityClass).newInstance();
shadowActivity = shadowOf_(activity);
shadowMainLooper = shadowOf_(Looper.getMainLooper());
}

at this moment mFragments member already null

then ActivityController.create called and controller tries to attach activity
and basically when Activity.attach is invoked crash happens.

@SuperJugy

Weird indeed.

I'm assuming this works on a real device. Also, mFragments must probably is a private or package local field that you should not possibly be able to set to null.

are you using a support library for your fragments?

I really have no idea what may be causing this.

@ryazmin

More debugging and as I can see the problem in ShadowMapActivity.
The initial issue (NPE) was fixed by changing the constructor method in ShadowMapActivity
(currently it's defined but does nothing, empty)

Changed to:
public void constructor() {
super.constructor();
}

Now activity was successfully created, but then I got SuperNotCalledException for onResume.
ShadowMapActivity implements onResume:

@Implementation
public void onResume() {
registerReceiver(connectivityBroadcastReceiver, new IntentFilter());
}

I tried to remove onResume implementation from ShadowMapActivity, but got
java.lang.RuntimeException: stub
at com.google.android.maps.MapActivity.onResume(Unknown Source)

So, I just changed it to:

@Implementation
public void onResume() {
registerReceiver(connectivityBroadcastReceiver, new IntentFilter());
field("mCalled").ofType(boolean.class).in(realActivity).set(true);
}

and seems now everything works, well, at-least for current simple test I used for debugging.

@ryazmin

so, as workaround, I created custom shadow from ShadowMapActivity with changes above,
and added it to org.robolectric.Config.properties as
shadows: com.mytests.shadows.ShadowMapActivityWorkaround

@jaredsburrows

@ryazmin Do you still need that Shadow with Robolectric 3.0?

If so, submit a PR.

@erd
Robolectric member
erd commented Jul 2, 2015

Closing this since it seems like there is a workaround.

@erd erd closed this Jul 2, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment