如何在Robolectric中模拟PackageManager

14

我的Android应用有一个简单的方法,可以启动一个意图来显示一个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();
    }
}
我正在使用 Robolectric 进行单元测试,但是我有些困难在验证这个方法。具体来说,getPackageManager() 方法总是返回 null。我该如何 shadow PackageManager?我尝试创建一个 ShadowPackageManager 并调用 bindShadowClass,但是我的代码没有被执行——getPackageManager() 总是返回 null。我还尝试 shadow 应用程序上下文并返回一个具体的 StubPackageManager,但是结果相同。也许我已经搜索/盯着屏幕太久了——有更好的方法来进行单元测试吗?

调用getPackageManager使用哪个上下文?你尝试过getApplicationContext().getPackageManager()吗? - Durairaj Packirisamy
7个回答

9
我将使用Robolectric 2.3进行翻译。正如其他答案中所指出的那样,getPackageManager()不会返回null,但是shadowApplication.setPackageManager已经不存在了。
由于您无法模拟PackageManager,因此无法给它一个要解决的Intents列表。幸运的是,Robolectric的PackageManager子类RobolectricPackageManager可以让您添加这些意图而无需模拟:
RobolectricPackageManager rpm = (RobolectricPackageManager)Robolectric.application.getPackageManager();
rpm.addResolveInfoForIntent(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS), new ResolveInfo());

2
感谢 Robolectric 3.4RobolectricPackageManager 已更改为 ShadowPackageManager pm = shadowOf(RuntimeEnvironment.application.getPackageManager()); - Ole

7

由于某些原因,你需要手动在应用程序中设置影子包管理器。 创建一个自定义测试运行程序(通过扩展RobolectricTestRunner),并覆盖setApplicationState方法:

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

然后在您的测试中指定您要使用自己的测试运行器:

@RunWith(MyTestRunner.class)
public class MyTest { ... }

谢谢@Jan,我们尝试了这个方法,它确实有效,但需要额外的阴影/模拟才能为queryIntentActivities生成响应。相反,我们选择在测试类中覆盖launchBrowser()并断言它被调用。 - mjancola

3

aleph_null的答案基础上,你可以使用ShadowResolveInfo.newResolveInfo()快速创建一个模拟ResolveInfo,以便立即使用(我正在使用Robolectric 2.4)。

RobolectricPackageManager rpm = (RobolectricPackageManager)Robolectric.application.getPackageManager();
rpm.addResolveInfoForIntent(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS), ShadowResolveInfo.newResolveInfo(...));

3

我升级到了最新的Robolectric(版本2.1.1),现在PackageManager不再返回null

下面的代码验证了浏览器意图是否被触发(并配置了一个url)。我没有在1.x上测试过,但我认为它在那里也可以工作:

@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" ) ) );
}

注意: 我在这里从一个碎片(fragment)中调用启动活动(start activity)


1
对于 Robolectric 3.1,您可以:
RobolectricPackageManager packageManager = RuntimeEnvironment.getRobolectricPackageManager();
// add necessary logic here

1

您可以在单独的方法中设置ShadowPackageManager(无需扩展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);
}

注意:这里我使用了Mockito,并在我的单元测试中模拟了PackageManager,而不是使用实际的PackageManager。

5
Robolectric 2.2(至少)不支持shadowApplication.setPackageManager() - Colin M.

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接