This class is quite useful but you may find yourself scratching your head because your test does not work like it should. This happens in particular if you’re doing some background work in your service, relying for example on AsyncTask for it.
Read on if you want to understand why it doesn’t work and find a solution for it.
In an Android application, any service is instantiated and operates on the main
thread. But this is not the case in the test framework provided by the
ServiceTestCase class. Your Service is instantiated in the same thread the
While your tests are running, there is no Looper waiting for messages on the service thread. In consequence, anything that relies on it and on the Handler class to communicate back to the main thread will not work.
AsyncTask uses a handler to ensure that the
method is called on the main thread. After
doInBackground has been called,
it posts a message on this handler, but as the
Looper on the service is not
running to handle the message, the
onPostExecute method will never be
To circumvent this behaviour, the service must be run on a separate thread with
Simulating main thread behaviour
ThreadServiceTestCase<T extends Service> (source here)
class that we describe here provides such features. It declares a
Looper and a
Hanlder to be able to run code on it:
In the setup of the test, we instantiate the service thread, start it, and link our handler with its looper:
1 2 3 4 5 6 7 8 9
tearDown method shuts down the tread.
We provide a
runOnServiceThread method to be able to run code on the service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
startService methods starts the service in its own thread:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
bound parameters tells wether to start the service with a binding or with
Intent.. The optional
ServiceRunnable parameter can be provided to add
some initialization code.
A test class using this code looks like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
With it, the service is started in its own thread and the
mechanism will work.
Waiting for listeners to be notified
A service that performs tasks asynchronously also notifies the outcome of the background tasks asynchronously. There are several techniques for doing that, but the most common are :
- Broadcast an intent, or
- Call a callback method on listeners.
This usually happens in the main thread. In our case, it would happen in the service thread. As the test is executing itself in its own thread, some synchronization mechanism is needed between the service thread and the test thread to be able to handle the outcome of the background task in the test.
ThreadServiceTestCase class provides an helper class for that:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
It contains a semaphore that can be used to synchronize the service thread with the test thread.
In the case of the callback listener, we can then define an utility class like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
When notified by the service in the service thread, the contained listener releases the semaphore and awakes the test that is waiting on the semaphore.
The listener contained in the helper class also needs to be added to the service being tested at service initialization. A test using this feature then becomes :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
You can grab The
ThreadServiceTestCase<T extends Service> source code here.
Hope it will help.