Can Android's ServiceTestCase send

2020-05-24 21:01发布

问题:

I want to test my bound service with ServiceTestCase. The testing consists of binding to MyBindServer, and sending a Message. Watching the logs, you can see the service is started when onBind() is called, and a message is sent from testAHello(), but, the server's handleMessage() is never called.

From the logs:

I/TestRunner( 2099): started: testAHello(com.inthinc.mybindserver.test.MyBindServerTest)
I/MyBindServerTest( 2099): setUp()
I/MyBindServer( 2099): onBind, action=com.inthinc.mybindserver.START
I/MyBindServerTest( 2099): testAHello
I/MyBindServerTest( 2099): sending SAY_HELLO
[here is where I expect to see the output from handleMessage()]
I/MyBindServerTest( 2099): tearDown()
I/TestRunner( 2099): finished:testAHello(com.inthinc.mybindserver.test.MyBindServerTest)
I/TestRunner( 2099): passed: testAHello(com.inthinc.mybindserver.test.MyBindServerTest)

Here is the code for MyBindServer.java:

package com.inthinc.mybindserver;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

public class MyBindServer extends Service {
    static final String TAG = "MyBindServer";
    public static final int MSG_SAY_HELLO = 1;
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            Log.i(TAG, String.format("handleMessage, what=%d", msg.what));
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Log.i(TAG, "hello");
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, String.format("onBind, action=%s", intent.getAction()));
        return mMessenger.getBinder();
    }

}

Here is the code for MyBindServerTest.java:

package com.inthinc.mybindserver.test;

import com.inthinc.mybindserver.MyBindServer;

import android.content.Intent;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.test.ServiceTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;

public class MyBindServerTest extends ServiceTestCase<MyBindServer> {
    private static final String TAG = "MyBindServerTest";
    Messenger mServer = null;

    public MyBindServerTest() {
        super(MyBindServer.class);
    }

    public MyBindServerTest(Class<MyBindServer> serviceClass) {
        super(serviceClass);
    }

    @Override
    public void setUp() {
        try {
            super.setUp();
            Log.i(TAG, "setUp()");
            Intent bindIntent = new Intent("com.inthinc.mybindserver.START");
            IBinder binder = bindService(bindIntent);
            assertNotNull(binder);
            mServer = new Messenger(binder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void tearDown() {
        try {
            super.tearDown();
            Log.i(TAG, "tearDown()");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @SmallTest
    public void testAHello() {
        Log.i(TAG, "testAHello");
        assertNotNull(mServer);
        Message msg = Message.obtain(null, MyBindServer.MSG_SAY_HELLO);
        Log.i(TAG, "sending SAY_HELLO");
        try {
            mServer.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}

回答1:

I was able to get this working using the procedure below..anyone is welcome to chime in if this is incorrect, but the example above works (i.e. MyBindServer's handler receives messages)

It seems as though ServiceTestCase's bindService() method intends to act like a local service. In this case, the goal is to test as a separate process, which means using the following instead of ServiceTestCase's bindService:

Intent bindIntent = new Intent(<registered intent>); //Where registered intent is declared in the manifest file
getContext().bindService(bindIntent,mConn,Context.BIND_AUTO_CREATE);

where mConn is a ServiceConnection object implemented to do whatever your test needs it to do, in the case above, set mServer.

With the above, MyBindServer's handleMessage() is called for the testAHello() test.

UPDATE: I have noticed that depending on how quickly the test processing is done, teardown() can be called before the binding is ready to use. In the case above adding control variables to throttle the program flow based on mConn's onServiceConnected being called provided consistent results.

E.g.

protected boolean bound = false;
protected boolean processed = false;
private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder service) {
        Log.i(TAG,"Service conn");

        mServer = new Messenger(service);
        if(mServer != null
                && mServer != null){
            bound = true;
        }
        processed = true;
    }

@Override
public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    Log.i(TAG,"Service Disconn");
}
};

Then add:

while(!processed){      
try {
   Thread.sleep(1000);
   } catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
   }
}   

to testAHello()