【问题标题】:How can I shadow the PackageManager with Robolectric如何使用 Robolectric 隐藏 PackageManager
【发布时间】:2012-10-15 19:38:08
【问题描述】:

我的 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。我怎样才能影响PackageManager?我尝试创建一个ShadowPackageManager 并调用bindShadowClass,但我的代码都没有被执行——getPackageManager() 总是返回null。我还尝试隐藏应用程序上下文并返回一个具体的StubPackageManager,但得到了相同的结果。 也许我一直在搜索/盯着太久 - 有没有更好的方法来单元测试这个方法?

【问题讨论】:

  • 哪个上下文用于调用getPackageManager?你试过 getApplicationContext().getPackageManager()

标签: android shadow robolectric


【解决方案1】:

我为此使用 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());

【讨论】:

  • 感谢Robolectric 3.4 RobolectricPackageManager 已更改为 ShadowPackageManager pm = shadowOf(RuntimeEnvironment.application.getPackageManager());
【解决方案2】:

出于某种原因,您需要在应用程序上手动设置影子包管理器。 创建一个自定义测试运行器(通过扩展 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() 并断言它已被调用。
【解决方案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" ) ) );
}

注意:我在这里从一个片段调用启动活动。

【讨论】:

    【解决方案4】:

    只需添加到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(...));
    

    【讨论】:

      【解决方案5】:

      您可以在单独的方法中设置 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。

      【讨论】:

      • Robolectric 2.2(至少),没有shadowApplication.setPackageManager()
      【解决方案6】:

      对于 Robolectric 3.1,您可以

      RobolectricPackageManager packageManager = RuntimeEnvironment.getRobolectricPackageManager();
      // add necessary logic here
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-19
        • 1970-01-01
        • 1970-01-01
        • 2016-07-08
        • 2019-10-04
        • 1970-01-01
        • 1970-01-01
        • 2015-12-19
        相关资源
        最近更新 更多