phong phong - 4 months ago 15
Java Question

unit test - why does my unit test fail on this code that uses finish(), handler, and thread?

Here is the activity I am testing.
I wish I could write it the other way since I heard using thread.sleep is dangerous. But I'm not allowed to do that.

Here is what this code does: when onCreate is called, MainActivity pauses for a couple of seconds, then it fires Activity2

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
goToAct2();
}
};

new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
handler.sendEmptyMessage(0);

} catch (InterruptedException e) {}
}
}).start();

}
private void goToAct2(){

Intent i = new Intent (this,Activity2.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();
}
}


I want to test if MainActivity calls the correct Activity (which is Activity2). Here is the unit test code:

public class MainActivityTest extends
ActivityInstrumentationTestCase2<MainActivity>
{

MainActivity activity;

public MainActivityTest()
{
super(MainActivity.class);
}

protected void setUp() throws Exception
{
super.setUp();
activity=getActivity();
}

@UiThreadTest
public void testOne()
{
ActivityMonitor Act2Monitor = getInstrumentation().addMonitor(Activity2.class.getName(), null, false);
getInstrumentation().callActivityOnCreate(activity, null);;
assertEquals(1, Act2Monitor.getHits());
}
}


I notice that:


  1. If I discard the handler-thread part:

    calling goToAct2() directly in onCreate(), the test still fails. But if I delete finish() in that method, the test passes.

  2. If I keep the handler-thread part:

    No matter what I do, the test keeps on failing even the actual activity works as it supposed to.



What is going on under the hood? And, how do I properly write a test that can confirm the creation of Activity2?

Answer

So, apparantly, getActivity() has a way of doing its work and callActivityOnCreate() is not what it seems.

I haven't read the source code of these, and from the sole behavior of the code, I come to these conclusions:

  • onCreate() of the activity under test most likely called is correctly by/via getActivity().
  • Invoking onCreate() with callActivityOnCreate() is unreliable especially where there is "pause" time (example: sleep, website loading).

Here is the code that works:

public class MainActivityTests extends
        ActivityInstrumentationTestCase2<MainActivity>
{
    MainActivity activity;
    long timeout;
    ActivityMonitor Act2Monitor;

    public MainActivityTests()
    {
        super(MainActivity.class);
    }

    protected void setUp() throws Exception
    {
        super.setUp();
        Act2Monitor = getInstrumentation()
                .addMonitor(Activity2.class.getName(), null, false);

        timeout = 4 * 1000;
        activity = getActivity();

        //Wait for it...
        try{
            Thread.sleep(timeout);
        }catch(Exception e){}
    }

    @UiThreadTest
    public void testCallActivity2()
    {
        assertEquals(1, Act2Monitor.getHits());    
    }

}
Comments