Android: unidade testando um serviço

Estou a tentar escrever um aplicativo Android usando TDD. Foi-me dada a tarefa de escrever um serviço que será muito importante na candidatura.

Por esta razão, estou a tentar fazer um teste adequado para o serviço. As diretrizes do Android dizem o seguinte:

o tópico O que testar lista considerações gerais para testar componentes Android. Aqui estão algumas diretrizes específicas para testar um serviço:

  • assegurar que a o oncreato() é chamado em resposta ao contexto.startService () ou Context.bindService (). Da mesma forma, você deve garantir que o onDestroy() é chamado em resposta ao contexto.stopService (), Context.unbindService (), stopelf (), or stopSelfResult (). Teste que o seu serviço lida corretamente com várias chamadas do contexto.startService (). Só a primeira chamada acciona o serviço.onCreate(), mas todas as chamadas desencadeiam uma chamada para o serviço.onStartCommand().

  • além disso, lembre - se que startService () as chamadas não fazem ninho, por isso uma única chamada para o contexto.stopService () ou Service.stopelf () (mas não stopelf (int)) parará o serviço. Você deve testar que seu serviço pára no ponto correto.

  • Teste qualquer lógica de negócio que o seu Serviço implementa. A lógica de negócios inclui a verificação de valores inválidos, cálculos financeiros e aritméticos, e assim por diante.

Fonte: Service Testing / Android Developers

ainda não vi um teste adequado para estes métodos de ciclo de vida, múltiplas chamadas para o contexto.startService () etc. Estou a tentar perceber isto, mas estou perdido.

Estou a tentar testar o serviço com a classe "ServiceTestCase".
import java.util.List;

import CoreManagerService;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Before;
import org.junit.Test;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.Context;
import android.content.Intent;
import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;

/**
 * 
 * This test should be executed on an actual device as recommended in the testing fundamentals.
 * http://developer.android.com/tools/testing/testing_android.html#WhatToTest
 * 
 * The following page describes tests that should be written for a service.
 * http://developer.android.com/tools/testing/service_testing.html
 * TODO: Write tests that check the proper execution of the service's life cycle.
 * 
 */
public class CoreManagerTest extends ServiceTestCase<CoreManagerService> {

    /** Tag for logging */
    private final static String TAG = CoreManagerTest.class.getName();

    public CoreManagerTest () {
        super(CoreManagerService.class);
    }

    public CoreManagerTest(Class<CoreManagerService> serviceClass) {
        super(serviceClass);

        // If not provided, then the ServiceTestCase will create it's own mock
        // Context.
        // setContext();
        // The same goes for Application.
        // setApplication();

        Log.d(TAG, "Start of the Service test.");
    }

    @SmallTest
    public void testPreConditions() {
    }

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
        super.setUp();
    }

    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @Test
    public void testStartingService() {
        getSystemContext().startService(new Intent(getSystemContext(), CoreManagerService.class));

        isServiceRunning();
    }

    private void isServiceRunning() {
        final ActivityManager activityManager = (ActivityManager)this.getSystemContext()
                .getSystemService(Context.ACTIVITY_SERVICE);
        final List<RunningServiceInfo> services = activityManager
                .getRunningServices(Integer.MAX_VALUE);

        boolean serviceFound = false;
        for (RunningServiceInfo runningServiceInfo : services) {
            if (runningServiceInfo.service.getClassName().equals(
                    CoreManagerService.class.toString())) {
                serviceFound = true;
            }
        }
        assertTrue(serviceFound);
    }
}
Estou a abordar isto incorrectamente? Devo utilizar um teste de actividade para testar a ligação do serviço?

Author: Julian Suarez, 2014-03-31

1 answers

Existe um exemplo usando o JUnit 4:

Serviço:

/**
 * {@link Service} that generates random numbers.
 * <p>
 * A seed for the random number generator can be set via the {@link Intent} passed to
 * {@link #onBind(Intent)}.
 */
public class LocalService extends Service {
    // Used as a key for the Intent.
    public static final String SEED_KEY = "SEED_KEY";

    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();

    // Random number generator
    private Random mGenerator = new Random();

    private long mSeed;

    @Override
    public IBinder onBind(Intent intent) {
        // If the Intent comes with a seed for the number generator, apply it.
        if (intent.hasExtra(SEED_KEY)) {
            mSeed = intent.getLongExtra(SEED_KEY, 0);
            mGenerator.setSeed(mSeed);
        }
        return mBinder;
    }

    public class LocalBinder extends Binder {

        public LocalService getService() {
            // Return this instance of LocalService so clients can call public methods.
            return LocalService.this;
        }
    }

    /**
     * Returns a random integer in [0, 100).
     */
    public int getRandomInt() {
        return mGenerator.nextInt(100);
    }
}

Teste:

public class LocalServiceTest {
    @Rule
    public final ServiceTestRule mServiceRule = new ServiceTestRule();

    @Test
    public void testWithBoundService() throws TimeoutException {
        // Create the service Intent.
        Intent serviceIntent =
                new Intent(InstrumentationRegistry.getTargetContext(), LocalService.class);

        // Data can be passed to the service via the Intent.
        serviceIntent.putExtra(LocalService.SEED_KEY, 42L);

        // Bind the service and grab a reference to the binder.
        IBinder binder = mServiceRule.bindService(serviceIntent);

        // Get the reference to the service, or you can call public methods on the binder directly.
        LocalService service = ((LocalService.LocalBinder) binder).getService();

        // Verify that the service is working correctly.
        assertThat(service.getRandomInt(), is(any(Integer.class)));
    }
}
 6
Author: Caipivara, 2017-03-26 23:01:12