Question

I want to test the following (standard?) scenario

  • An activity uses a Service object to do some stuff.
  • The Service is bind to the activity with bindService(..).
  • The service itself allows access via a Binder that it returns in onBind() that gives access to the service object itself (everything happens on the same thread here).
  • In onServiceConnected(..) the activity gets access to the service via the Binder and hands the Service a Handler object to send Messages to the activity.
  • So the communication is: Activity -> Service via direct calls on Service. And Service -> Activity via Handler object injected from Activity into Service

This scenario works fine. No problems in the App.

But now I also want to write automated Unit Tests and I run into the problem, that the handler does not seem to send and process the messages any more.

I wrote an Android JUnit Test like this

public class EpcServiceMockTest extends ServiceTestCase<EpcServiceMock> {...}

With a test method like this

public void testConnectToDevice() {
    Intent serviceIntent = new Intent(getContext(), EpcServiceMock.class);
    IBinder binder = bindService(serviceIntent);
    EpcServiceHandlerMock mockHandler = new EpcServiceHandlerMock(this);
    EpcServiceMock service = (EpcServiceMock) ((EpcServiceBase.LocalBinder) binder).getService(mockHandler);
service.doSomething(..);

This set-up works fine. The service runs and does execute the called methods. BUT everytime the handler is used to send messages, the messages are never executed. i.e. handleMessage() is never called on the Handler.

    Message callbackMsg = mCallbackHandler.obtainMessage(CB_CONNECT_TO_NULL_DEVICE, mDevice);
    mCallbackHandler.sendMessage(callbackMsg);

This code is called. The Handler mCallbackHandler is the correct type. No errors. Just nothing happens. No call to handleMessage() in the Handler.

First I thought there is a race condition, because the Looper is on another thread. So I let the test case sleep for a while:

(..)
service.doSomething(..);
Thread.sleep(3000);
Log.d(TAG, "Waking up ...");
callbacks = mockHandler.receivedCallbacks;
assertEquals("one callback", 1, callbacks.size());

But still nothing. The handleMessage() method in the handler is never called in the test scenario. But it works fine in the real App.

Is there a fundamental problem in testing handlers in Android JUnit Tests?

Was it helpful?

Solution

Problem solved:

I was so stupid to not to start the Handler on a separate thread. So the Handler run on the same thread as the JUnit Test. Therefore the JUnit finished first, seeing no results from the Handler and fails.

When I put the Handler on a different thread everything works fine in the JUnit Tests.

OTHER TIPS

Alternatively you can mock synchronous Handler instead of Thread.sleep

Mockito.when(testHandler.post(any(Runnable.class))).thenAnswer(new Answer<Object>() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
            ((Runnable) invocation.getArgument(0)).run();
            return null;
        }
    });
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top