Как протестировать IntentService с помощью Robolectric? - программирование

Как протестировать IntentService с помощью Robolectric?

Я пытаюсь протестировать метод onHandleIntent() IntentService с помощью Robolectric.

Я запускаю службу с помощью:

Activity activity = new Activity();
Intent intent = new Intent(activity, MyService.class);
activity.startService(intent);

ShadowActivity shadowActivity = Robolectric.shadowOf(activity);
Intent startedIntent = shadowActivity.getNextStartedService();
assertNotNull(startedIntent);

похоже, что startedIntent не является нулевым, но onHandleIntent(), похоже, не называется.

как я должен его проверить?

4b9b3361

Ответ 1

onHandleIntent - защищенный метод, поэтому он не может быть вызван напрямую.

Мое решение состояло в том, чтобы расширить класс службы в моем тестовом случае, переопределить onHandleIntent, сделав его общедоступным и вызвав super.onHandleIntent(intent)

затем вызовите onHandleIntent непосредственно из тестового примера.

Ответ 2

Robolectric имеет ServiceController, который может проходить через жизненный цикл службы так же, как и активность. Этот контроллер предоставляет все методы для выполнения соответствующих обратных вызовов службы (например, controller.attach().create().startCommand(0, 0).destroy()).

Теоретически мы можем ожидать, что IntentService.onStartCommand() вызовет IntentService.onHandleIntent(Intent) через свой внутренний Handler. Однако в этом Handler используется Looper, который работает в фоновом потоке, и я не знаю, как сделать этот поток для перехода к следующей задаче. Обходным решением было бы создать TestService, который имитирует одно и то же поведение, но запускает onHandleIntent(Intent) в основном потоке (поток, используемый для запуска тестов).

@RunWith(RobolectricGradleTestRunner.class)
public class MyIntentServiceTest {
    private TestService service;
    private ServiceController<TestService> controller;

    @Before
    public void setUp() {
        controller = Robolectric.buildService(TestService.class);
        service = controller.attach().create().get();
    }

    @Test
    public void testWithIntent() {
        Intent intent = new Intent(RuntimeEnvironment.application, TestService.class);
        // add extras to intent
        controller.withIntent(intent).startCommand(0, 0);
        // assert here
    }

    @After
    public void tearDown() {
        controller.destroy();
    }

    public static class TestService extends MyIntentService {
        public boolean enabled = true;

        @Override
        public void onStart(Intent intent, int startId) {
            // same logic as in internal ServiceHandler.handleMessage()
            // but runs on same thread as Service
            onHandleIntent(intent);
            stopSelf(startId);
        }
    }
}

UPDATE. Альтернативно, достаточно просто создать аналогичный контроллер для IntentService, как показано ниже:

public class IntentServiceController<T extends IntentService> extends ServiceController<T> {
    public static <T extends IntentService> IntentServiceController<T> buildIntentService(Class<T> serviceClass) {
        try {
            return new IntentServiceController<>(Robolectric.getShadowsAdapter(), serviceClass);
        } catch (IllegalAccessException | InstantiationException e) {
            throw new RuntimeException(e);
        }
    }

    private IntentServiceController(ShadowsAdapter shadowsAdapter, Class<T> serviceClass) throws IllegalAccessException, InstantiationException {
        super(shadowsAdapter, serviceClass);
    }

    @Override
    public IntentServiceController<T> withIntent(Intent intent) {
        super.withIntent(intent);
        return this;
    }

    @Override
    public IntentServiceController<T> attach() {
        super.attach();
        return this;
    }

    @Override
    public IntentServiceController<T> bind() {
        super.bind();
        return this;
    }

    @Override
    public IntentServiceController<T> create() {
        super.create();
        return this;
    }

    @Override
    public IntentServiceController<T> destroy() {
        super.destroy();
        return this;
    }

    @Override
    public IntentServiceController<T> rebind() {
        super.rebind();
        return this;
    }

    @Override
    public IntentServiceController<T> startCommand(int flags, int startId) {
        super.startCommand(flags, startId);
        return this;
    }

    @Override
    public IntentServiceController<T> unbind() {
        super.unbind();
        return this;
    }

    public IntentServiceController<T> handleIntent() {
        invokeWhilePaused("onHandleIntent", getIntent());
        return this;
    }
}

Ответ 3

Я думаю, вы подошли к этому неправильно. Вы тестируете здесь:

Intent startedIntent = shadowActivity.getNextStartedService();
assertNotNull(startedIntent);

который был вызван startService().

Затем вы должны предположить, что Android правильно выполнил свой конец и после вызова startService() служба действительно будет запущена.

Я думаю, что если вы хотите проверить поведение onHandleIntent(), вы должны просто вызвать его напрямую.

Я точно не уверен... но я думаю, что тест, который вы хотите написать, просто будет тестировать платформу Android. Вы должны написать два теста.

1) Проверьте, что startService() вызывается (как в вашем примере).

2) Проверьте поведение onHandleIntent() (вызывая его напрямую).

У меня очень мало опыта работы с Robolectric, но, надеюсь, это полезно.

Приветствия

Ответ 4

Я использую этот

@Before
public void setup(){
    mainActivity = Robolectric.buildActivity(MainActivity.class)
            .create()
            .start()
            .resume()
            .get();
    context = Robolectric.getShadowApplication().getApplicationContext();
    bt_start = (Button) mainActivity.findViewById(R.id.button);
    bt_stop = (Button) mainActivity.findViewById(R.id.button2);
}

@Test
public void serviceIsOn(){

    bt_start.performClick();
    Intent intent = Robolectric.shadowOf(mainActivity).peekNextStartedService();
    assertEquals(MiService.class.getCanonicalName(),intent.getComponent().getClassName());
}

а затем я запускаю тест, и все в порядке

Ответ 5

Robolectric 3.1 +

Создайте экземпляр своей службы

Вызов onHandleIntent напрямую

    YourService svc = Robolectric.buildService(YourService.class).get();
    Intent fakeIntent = new Intent();
    fakeIntent.putExtra(YourService.SOME_EXTRA, "debug|fail");

    svc.onHandleIntent(fakeIntent);

    assertThat(something.happened).isEqualTo(true);

Примечание. Используйте Robolectric.buildService (не .buildIntentService), даже если вы тестируете IntentService. .buildIntentService в настоящее время не работает, насколько я могу судить.

Обновление 2017-05-17:

Проблема с buildIntentService будет исправлена ​​в будущем