Как я могу затенить PackageManager с помощью Robolectric

В моем приложении для 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, но получил те же результаты. Может быть, я слишком долго искал/смотрел - есть ли лучший способ модульного тестирования этого метода?


person mjancola    schedule 15.10.2012    source источник
comment
какой контекст используется для вызова getPackageManager? Вы пробовали getApplicationContext().getPackageManager()   -  person Durairaj Packirisamy    schedule 16.10.2012


Ответы (6)


Я использую Robolectric 2.3 для этого. Как отмечалось в других ответах, getPackageManager() не возвращает значение null, но shadowApplication.setPackageManager больше не существует.

Поскольку вы не можете издеваться над PackageManager, вы не можете дать ему список Intents для разрешения. К счастью, подкласс PackageManager Robolectric, RobolectricPackageManager, позволяет вам добавлять эти намерения без имитации:

RobolectricPackageManager rpm = (RobolectricPackageManager)Robolectric.application.getPackageManager();
rpm.addResolveInfoForIntent(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS), new ResolveInfo());
person aleph_null    schedule 15.06.2014
comment
Спасибо, для Robolectric 3.4 RobolectricPackageManager изменено на ShadowPackageManager pm = shadowOf(RuntimeEnvironment.application.getPackageManager()); - person Ole; 17.02.2018

По какой-то причине вам нужно вручную установить теневой менеджер пакетов в вашем приложении. Создайте собственный тест-раннер (путем расширения 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 { ... }
person Jan Berkel    schedule 17.10.2012
comment
Спасибо @Jan, мы попробовали это, и это сработало, но потребовались бы дополнительные тени / макеты, чтобы иметь возможность генерировать ответ для queryIntentActivities. Вместо этого мы решили переопределить launchBrowser() в тестовом классе и утвердить его вызов. - person mjancola; 26.10.2012

Я обновился до последней версии 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" ) ) );
}

Примечание. Здесь я запускаю активность из фрагмента.

person colabug    schedule 13.06.2013

Просто добавьте к ответу 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(...));
person hidro    schedule 31.01.2015

Вы можете установить 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.

person Sri    schedule 09.06.2013
comment
Robolectric 2.2 (по крайней мере) не имеет shadowApplication.setPackageManager() - person Colin M.; 10.06.2014

Для Robolectric 3.1 вы можете

RobolectricPackageManager packageManager = RuntimeEnvironment.getRobolectricPackageManager();
// add necessary logic here
person pixel    schedule 17.11.2016