Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Unit Testing a service

I'm currently trying to write an Android app using TDD. I've been given the assignment to write a service that will be very important in the application.

As for this reason I'm trying to write a proper test for the service. The Android guidelines state the following:

The topic What To Test lists general considerations for testing Android components. Here are some specific guidelines for testing a Service:

  • Ensure that the onCreate() is called in response to Context.startService() or Context.bindService(). Similarly, you should ensure that onDestroy() is called in response to Context.stopService(), Context.unbindService(), stopSelf(), or stopSelfResult(). Test that your Service correctly handles multiple calls from Context.startService(). Only the first call triggers Service.onCreate(), but all calls trigger a call to Service.onStartCommand().

  • In addition, remember that startService() calls don't nest, so a single call to Context.stopService() or Service.stopSelf() (but not stopSelf(int)) will stop the Service. You should test that your Service stops at the correct point.

  • Test any business logic that your Service implements. Business logic includes checking for invalid values, financial and arithmetic calculations, and so forth.

Source: Service Testing | Android Developers

I've yet to see a proper test for these life cycle methods, multiple calls to Context.startService() etc. I'm trying to figure this out but I'm currently at a loss.

I'm trying to test the service with the ServiceTestCase class:

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

Am I approaching this incorrectly? Should I use an activity test to test the binding of the service against?

like image 774
Orion Avatar asked Mar 31 '14 07:03

Orion


People also ask

What is Android unit testing?

Unit tests or small tests only verify a very small portion of the app, such as a method or class. End-to-end tests or big tests verify larger parts of the app at the same time, such as a whole screen or user flow. Medium tests are in between and check the integration between two or more units.

What is unit testing with example Android?

JUnit is a “Unit Testing” framework for Java Applications which is already included by default in android studio. It is an automation framework for Unit as well as UI Testing. It contains annotations such as @Test, @Before, @After, etc. Here we will be using only @Test annotation to keep the article easy to understand.

How can write unit test cases for activity in Android?

unit tests can be written under test java package, and instrumental tests can be written under androidTest package. You can then access UI widgets using Espresso ViewMatchers and then you can apply actions on them using ViewActions . Check documentation for further help. Show activity on this post.


1 Answers

Theres an example using JUnit 4:

Service:

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

Test:

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)));
    }
}
like image 174
Daniel Gomez Rico Avatar answered Sep 22 '22 08:09

Daniel Gomez Rico