7

I am trying to write some tests for an existing application. I wanted to supply a test Application class to the tests and I followed the example here, since I am also using Dagger for DI.

However, if the activity under test is an ActionBarActivity, I get the following exception:

java.lang.IllegalArgumentException: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{mypackage.view.activity/mypackage.view.activity.MyActivity}
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:282)
at android.support.v7.app.ActionBarActivityDelegate.onCreate(ActionBarActivityDelegate.java:116)
at android.support.v7.app.ActionBarActivityDelegateICS.onCreate(ActionBarActivityDelegateICS.java:57)
at android.support.v7.app.ActionBarActivity.onCreate(ActionBarActivity.java:98)
at mypackage.view.activity.MyActivity.onCreate(MyActivity.java:68)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.test.ActivityUnitTestCase.startActivity(ActivityUnitTestCase.java:158)
at mypackage.MyActivityTest.test(MyActivityTest.java:89)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Caused by: android.content.pm.PackageManager$NameNotFoundException: ComponentInfo{mypackage.view.activity/mypackage.view.activity.MyActivity}
at android.app.ApplicationPackageManager.getActivityInfo(ApplicationPackageManager.java:242)
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:298)
at android.support.v4.app.NavUtils.getParentActivityName(NavUtils.java:279)
... 21 more

My test class looks like follows:

public class MyActivityTest extends ActivityUnitTestCase<MyActivity> {

    ...

    public MyActivityTest() {
        super(MyActivity.class);
    }

    private Context context;

    private TestBaseApplication application;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        context = new ContextThemeWrapper( getInstrumentation().getTargetContext(), R.style.Theme_AppCompat){
            @Override
            public Context getApplicationContext() {
                return application;
            }
        };
        application = new TestBaseApplication( context);
        setApplication(application);

        ...
    }

    public void test() throws InterruptedException {
        setActivityContext( context);
        Intent intent = new Intent( context, MyActivity.class);
        startActivity(intent, null, null);
        ...
    }
}

The activity appears in the AndroidManifest as follows:

<activity
            android:name=".view.activity.MyActivity"
            android:icon="@drawable/actionbar_logo"
            android:screenOrientation="portrait"
            android:parentActivityName="mypackage.ParentActivity">
            <meta-data android:name="android.support.PARENT_ACTIVITY"
                android:value="mypackage.ParentActivity"/>
        </activity>

After some troubleshooting, I tried running the example at the link above and it works just fine, even when I change the activity to extend ActionBarActivity.

Since I wasn't able to find the cause of the problem, I also played around with the manifest, build.gradle, etc. Now, I am just stuck with this and I cannot think of anything else.

This post may also be close to related to the problem, but there is not any comment on that one either. And this post also seemed to have similar problem but the solution there doesn't work me as I don't want the real application to be launched with the activity.

EDIT:

I've created a simple separate project, in order to isolate the issue.

First I've written an ActivityUnitTestCase for an Activity which extends ActionBarActivity. It worked fine.

After that, I've tried adding few more activities, making them parent of each other.( so that it looks like my actual project). And that worked fine too.

Lastly, I've added more ActivityUnitTestCase classes with different activities from my actual project, all extending ActionBarActivity, with same setup for all of them , and run the tests on two devices, one being an emulator (Genymotion) and the other is my physical device. (Nexus 4)

Tests all gave the NameNotFoundException on the emulator. All but one of the tests passed on the physical device, which made me even more confused.

Community
  • 1
  • 1
Emre Dirican
  • 397
  • 5
  • 15
  • 1
    If I had to guess, it's a bug in the way AppCompat looks up the parent activity, only seen when using a `Context` from an instrumentation test. But that's just a guess. You might try creating a small separate project (and test) to see if you can reproduce it in isolation. If you can, and you're sure you're on the latest version of `appcompat-v7`, file an issue on [the issue tracker](http://b.android.com) with the sample project and stack trace. – CommonsWare Jul 16 '14 at 22:17
  • @CommonsWare: Thanks for the comment. Updated my post with the results of your suggestions. There might be a bug like you suggest but definitely cannot reproduce it in a separate project, such that I can file an issue. – Emre Dirican Jul 17 '14 at 14:22
  • Unfortunately, that also limits anyone's ability to help, as we aren't likely going to be able to reproduce it either. :-( – CommonsWare Jul 17 '14 at 15:57

1 Answers1

10

It all comes down to this bug in ActivityUnitTestCase.

Activities built using ActivityUnitTestCase.startAcitvity() have a componentName pointing to the application package. So when ActionBarActivity checks for a parent activity to update the Up symbol, the test crashes if the activity is not in the app 'root' package.

Fortunately the workaround proposed in the issue description works just fine, so until this is fixed, just make a local copy of ActivityUnitTestCase, update the line in which the componentName is defined as below, and make sure your test cases extend that class instead of the original ActivityUnitTestCase.

// replace this line
new ComponentName(mActivityClass.getPackage().getName(), mActivityClass.getName());

// with this
new ComponentName(getInstrumentation().getTargetContext(), mActivityClass);
Jay Wick
  • 12,325
  • 10
  • 54
  • 78
ivagarz
  • 2,434
  • 22
  • 22
  • 1
    Thanks for this, it actually helped me. :) I just copied ActivityUnitTestCase and changed the line to "ComponentName cn = new ComponentName(getInstrumentation().getTargetContext(), mActivityClass);" – scrrr Nov 29 '14 at 15:45
  • Or you can move the activity to the root package, but that solution may be less than ideal – DJ_Polly Jun 28 '15 at 07:57