Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing that an Activity returns the expected result

I have the following Activity:

package codeguru.startactivityforresult;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ChildActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.child);

        this.resultButton = (Button) this.findViewById(R.id.result_button);
        this.resultButton.setOnClickListener(onResult);
    }

    private View.OnClickListener onResult = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent result = new Intent();
            result.putExtra(ChildActivity.this.getString(R.string.result), ChildActivity.this.getResources().getInteger(R.integer.result));
            ChildActivity.this.setResult(RESULT_OK, result);
            ChildActivity.this.finish();
        }
    };
    private Button resultButton = null;
}

And the following JUnit test:

package codeguru.startactivityforresult;

import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.widget.Button;
import junit.framework.Assert;

public class ChildActivityTest extends ActivityInstrumentationTestCase2<ChildActivity> {

    public ChildActivityTest() {
        super(ChildActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();

        this.setActivityInitialTouchMode(false);

        this.activity = this.getActivity();
        this.resultButton = (Button) this.activity.findViewById(R.id.result_button);
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @UiThreadTest
    public void testResultButtonOnClick() {
        Assert.assertTrue(this.resultButton.performClick());
        Assert.fail("How do I check the returned result?");
    }
    private Activity activity;
    private Button resultButton;
}

How do I make sure that clicking the button sets the correct result (with the call to setResult()) that will be returned to any activity which starts this acitivity with startActivityForResult()?

like image 450
Code-Apprentice Avatar asked Oct 24 '12 01:10

Code-Apprentice


2 Answers

With current Activity implementation in the question, i.e. by clicking button in ChildActivity set the result then destroy the activity immediately, there are not much we can do in the ChildActivityTest for testing result related stuff.

The answer in related question Testing onActivityResult() shows how to unit-test startActivityForResult() and/or onActivityResult() standalone in MainActivityTest. By standalone means MainActivityTest does not depend on ChildActivity's interaction, instrumentation will capture ChildActivity creation and kill it immediately then return a ready baked mock ActivityResult, hence unit test MainActivity.

If you don't want instrumentation interrupt and return the mock ActivityResult, you can let ChildActivity keep going then simulating the interaction in ChildActivity consequently and return the real ActivityResult back to MainActivity. Says if you MainActivity start ChildActivity for result then update a TextView, to test the the whole end-to-end interaction/cooperation, see sample code below:

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
  ... ...

  public void testStartActivityForResult() {
    MainActivity mainActivity = getActivity();
    assertNotNull(activity);

    // Check initial value in TextView:
    TextView text = (TextView) mainActivity.findViewById(com.example.R.id.textview1);
    assertEquals(text.getText(), "default vaule");

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    // Create an ActivityMonitor that monitor ChildActivity, do not interrupt, do not return mock result:
    Instrumentation.ActivityMonitor activityMonitor = getInstrumentation().addMonitor(ChildActivity.class.getName(), null , false);

    // Simulate a button click in MainActivity that start ChildActivity for result:
    final Button button = (Button) mainActivity.findViewById(com.example.R.id.button1);
    mainActivity.runOnUiThread(new Runnable() {
      public void run() {
        button.performClick();
      }
    });

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    getInstrumentation().waitForIdleSync();
    ChildActivity childActivity = (ChildActivity) getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 5);
    // ChildActivity is created and gain focus on screen:
    assertNotNull(childActivity);

    // Simulate a button click in ChildActivity that set result and finish ChildActivity:
    final Button button2 = (Button) childActivity.findViewById(com.example.R.id.button1);
    childActivity.runOnUiThread(new Runnable() {
      public void run() {
        button2.performClick();
      }
    });

    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    getInstrumentation().waitForIdleSync();
    // TextView in MainActivity should be changed:
    assertEquals(text.getText(), "default value changed");
  }

  ... ...
}

I add three Thread.sleep() calls here so that you can get a chance see the button clicking simulation when running JUnit Test. As you can see here, a standalone ChildActivityTest is not sufficient to test the whole cooperation, we are actually testing ChildActivity.setResult() indirectly via MainActivityTest, as we need simulate the whole interaction from the very beginning.

like image 194
yorkw Avatar answered Nov 15 '22 12:11

yorkw


May I recommend using Robotium framework. Robotium uses a class Solo, which has a very helpful API .

It allows you to see what the current activity is. Allows you to assert that an activity has started etc.

http://code.google.com/p/robotium/

After performing click you can do something like

 void assertCurrentActivity(java.lang.String message, java.lang.Class expectedClass, boolean isNewInstance)         So heres what I would do. After the code to click the button

Activity callingActvity  = solo.getCurrentActivity();
  solo.assertCurrentActivity("ShouldbeCallingActivity","NameOfCallingActivity");

I cannot give a full solution without knowing how the callback works. But assuming that some text shows up for RESULT_OK as opposed to some other text ,

you can do something like

assertTrue(solo.waitForText("Text that is supposed to show for Result OK");

PS: robotium will only work for activities in same application.

like image 31
smk Avatar answered Nov 15 '22 11:11

smk