可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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 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 activities = new ArrayList(); 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