Pregunta ¿Cómo probar un IntentService con Robolectric?


Estoy tratando de probar el onHandleIntent() método de una IntentService utilizando Robolectric.

Estoy comenzando el servicio con:

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

parece startedIntent no es nulo, pero onHandleIntent() no parece ser llamado.

¿cómo debería probarlo?


18
2017-08-19 09:51


origen


Respuestas:


Robolectric tiene un ServiceController eso puede pasar por el ciclo de vida del servicio al igual que la actividad. Este controlador proporciona todos los métodos para ejecutar las devoluciones de llamada de servicio correspondientes (p. controller.attach().create().startCommand(0, 0).destroy())

Teóricamente podemos esperar que IntentService.onStartCommand() activará IntentService.onHandleIntent(Intent), a través de su interno Handler. Sin embargo esto Handler usa un Looper que se ejecuta en un hilo de fondo, y no tengo idea de cómo hacer que este hilo avance a la siguiente tarea. Una solución alternativa sería crear TestService que imita el mismo comportamiento, pero desencadena onHandleIntent(Intent) en el hilo principal (hilo utilizado para ejecutar las pruebas).

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

ACTUALIZAR: Alternativamente, es bastante sencillo crear un controlador similar para IntentService, de la siguiente manera:

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

9
2017-11-17 11:13



onHandleIntent es un método protegido por lo que no se puede invocar directamente.

mi solución fue extender la clase de servicio en mi caso de prueba, anular onHandleIntent haciéndolo público y llamando super.onHandleIntent(intent)

luego llame onHandleIntent directamente desde el caso de prueba.


6
2017-09-29 15:44



Creo que te has acercado a esto de la manera incorrecta. Estás probando aquí:

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

ese startService()fue llamado.

Debe suponer que Android ha implementado su final correctamente y que después de llamar startService() el servicio se iniciará.

Creo que si quieres probar el comportamiento de onHandleIntent(), solo debes llamarlo directamente?

No estoy seguro exactamente ... pero creo que la prueba que desea escribir sería simplemente probar el marco de Android. Deberías escribir dos pruebas.

1) Prueba eso startService() se llama (como lo has hecho en tu ejemplo).

2) Pruebe el comportamiento de onHandleIntent() (llamándolo directamente).

Tengo muy poca experiencia con Robolectric pero espero que esto sea útil.

Aclamaciones


1
2017-09-29 11:25



yo uso esto

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

y luego corro la prueba y todo está bien


1
2018-02-10 20:39



Robolectric 3.1+

Crea una instancia de tu servicio

Llame aHandleIntent directamente

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

Nota: uso Robolectric.buildService (no .buildIntentService) incluso si está probando un IntentService. .buildIntentService Actualmente no está funcionando, por lo que yo sé.

Actualización 2017-05-17:

Problema con buildIntentService estarán arreglado en el futuro


1
2018-05-11 17:07