问题
My Android application has a simple method to fire off an intent to display a URL.
protected void launchBrowser(int id)
{
Uri uri = Uri.parse( getString( id ) );
Intent intent = new Intent( ACTION_VIEW, uri);
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
if (activities.size() > 0)
{
startActivity(intent);
}
else
{
Toast.makeText(getApplicationContext(),
"ERROR - no application to display a web page",
Toast.LENGTH_SHORT).show();
}
}
I'm using Robolectric for unit testing but I'm having trouble verifying this method. Specifically, getPackageManager()
is always returning null. How I can shadow the PackageManager
? I tried creating a ShadowPackageManager
and calling bindShadowClass
, but none of my code gets executed - getPackageManager()
always returns null
. I also tried to Shadow the Application context and return a concrete StubPackageManager
, but got the same results.
Maybe I've been searching/staring too long - is there a better way to unit test this method?
回答1:
I'm using Robolectric 2.3 for this. As noted in other answers, getPackageManager()
does not return null, but shadowApplication.setPackageManager
no longer exists.
Since you can't mock PackageManager
, you can't give it a list of Intents
to resolve for. Fortunately, Robolectric's PackageManager
subclass, RobolectricPackageManager
, can let you add these intents without a mock:
RobolectricPackageManager rpm = (RobolectricPackageManager)Robolectric.application.getPackageManager();
rpm.addResolveInfoForIntent(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS), new ResolveInfo());
回答2:
For some reason you need to set the shadow packagemanager manually on your application. Create a custom test runner (by extending RobolectricTestRunner) and override the setApplicationState method:
public class MyTestRunner extends RobolectricTestRunner {
@Override
public void setupApplicationstate(RobolectricConfig robolectricConfig) {
super.setupApplicationState(robolectricConfig);
ShadowApplication shadowApplication = shadowOf(Robolectric.application);
shadowApplication.setPackageName(robolectricConfig.getPackageName());
shadowApplication.setPackageManager(new RobolectricPackageManager(Robolectric.application, robolectricConfig));
}
}
Then specify in your tests that you want to use your own test runner:
@RunWith(MyTestRunner.class)
public class MyTest { ... }
回答3:
I upgraded to the latest Robolectric (version 2.1.1) and the PackageManager doesn't come up null
anymore.
The code below verifies that a browser intent is fired (and configured with a url). I haven't tested this on 1.x, but I think it works there too:
@Test
public void recipeTitleShouldOpenBrowserOnClick() throws Exception
{
title.performClick();
ShadowActivity shadowActivity = shadowOf( detailFragment.getActivity() );
ShadowActivity.IntentForResult intent = shadowActivity.peekNextStartedActivityForResult();
// or
// ShadowActivity.IntentForResult intent = shadowActivity.getNextStartedActivityForResult();
assertThat( intent.intent,
equalTo( createBrowserIntentWithURL( "url" ) ) );
}
Note: I'm invoking start activity from a fragment here.
回答4:
Just add on to aleph_null's answer, you can use ShadowResolveInfo.newResolveInfo()
to quickly create a mock ResolveInfo
ready to be used (I'm using Robolectric 2.4).
RobolectricPackageManager rpm = (RobolectricPackageManager)Robolectric.application.getPackageManager();
rpm.addResolveInfoForIntent(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS), ShadowResolveInfo.newResolveInfo(...));
回答5:
You can set the ShadowPackageManager in a separate method (without extending RobolectricTestRunner)
private void setupPackageManager() {
ShadowApplication shadowApplication = shadowOf(Robolectric.application);
shadowApplication.setPackageManager(mockPackageManager);
List<ResolveInfo> activities = new ArrayList<ResolveInfo>();
activities.add(resolveInfo("com.test.package.Class1"));
activities.add(resolveInfo("com.test.package.Class2"));
when(mockPackageManager.queryIntentActivities(any(Intent.class), anyInt())).thenReturn(activities);
}
Note : Here, i have used mockito & mocked the packagemanager for my unittest instead of using the actual PackageManager.
回答6:
For Robolectric 3.1 you can
RobolectricPackageManager packageManager = RuntimeEnvironment.getRobolectricPackageManager();
// add necessary logic here
来源:https://stackoverflow.com/questions/12902777/how-can-i-shadow-the-packagemanager-with-robolectric