Testing onActivityResult()

12,539

Solution 1

Use intents framework to mock the activity result

intending(hasComponent(DummyActivity.class.getName())).respondWith(new ActivityResult(resultCode, dataIntent));
rule.getActivity().startActivityForResult(new Intent(context,DummyActivity.class));

verify on activity result logic

Solution 2

For testing onActivityResult() in your test class, all you need to do is:

  1. Create an ActivityMonitor which catching ChildActivity creation and retuning the mock ActivityResult.
  2. Simulating the button click which start the ChildActivity for result.
  3. Do some assertion on status and the mock ActivityResult.

Sample StartActivityForResult:

public class StartActivityForResult extends Activity {
  private boolean activityResultIsReturned = false;
  private String activityResult = null;

  ... ...

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    activityResultIsReturned = true;
    activityResult = data.getStringExtra("result");

    ... ...
  }

  ... ...
}

Sample StartActivityForResultTest:

public class StartActivityForResultTest extends ActivityInstrumentationTestCase2<StartActivityForResult> {
  ... ...

  public void testOnActivityResult() {
    // Get current Activity and check initial status:
    StartActivityForResult myActivity = getActivity();
    assertFalse(myActivity.getActivityResultIsReturned());
    assertNull(myActivity.getActiityResult());

    // Mock up an ActivityResult:
    Intent returnIntent = new Intent();
    returnIntent.putExtra("result", "This is the result");
    Instrumentation.ActivityResult activityResult = new Instrumentation.ActivityResult(Activity.RESULT_OK, returnIntent);

    // Create an ActivityMonitor that catch ChildActivity and return mock ActivityResult:
    Instrumentation.ActivityMonitor activityMonitor = getInstrumentation().addMonitor(ChildActivity.class.getName(), activityResult , true);

    // Simulate a button click that start ChildActivity for result:
    final Button button = (Button) myActivity.findViewById(com.company.R.id.open_next_activity);
    myActivity.runOnUiThread(new Runnable() {
      @Override
      public void run() {
        // click button and open next activity.
        button.performClick();
      }
    });

    // Wait for the ActivityMonitor to be hit, Instrumentation will then return the mock ActivityResult:
    ChildActivity childActivity = getInstrumentation().waitForMonitorWithTimeout(activityMonitor, 5);

    // How do I check that StartActivityForResult correctly handles the returned result?
    assertTrue(myActivity.getActivityResultIsReturned());
    assertEqual(myActivity.getActiityResult(), "This is the result");
  }

  ... ...
}
Share:
12,539

Related videos on Youtube

Code-Apprentice
Author by

Code-Apprentice

I primarily program in C++ and Java. Recently I started learning Haskell. My current mathematical interests are group theory, graph theory, category theory, and type theory. I also enjoy playing chess and Go. My Amazon wishlist

Updated on September 15, 2022

Comments

  • Code-Apprentice
    Code-Apprentice over 1 year

    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;
    import android.widget.Toast;
    
    public class StartActivityForResult extends Activity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            this.startButton = (Button) this.findViewById(R.id.start_button);
            this.startButton.setOnClickListener(onStart);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            int result = data.getIntExtra(StartActivityForResult.this.getString(R.string.result), -1);
            String msg = "requestCode=" + requestCode + ", resultCode=" + resultCode + ", result=" + result;
            Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
        }
    
        private View.OnClickListener onStart = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(StartActivityForResult.this, ChildActivity.class);
    
                StartActivityForResult.this.startActivityForResult(intent, R.id.child_request);
            }
        };
        private Button startButton = null;
    }
    

    And the following JUnit test:

    package codeguru.startactivityforresult;
    
    import android.app.Activity;
    import android.app.Instrumentation;
    import android.test.ActivityInstrumentationTestCase2;
    import android.test.UiThreadTest;
    import android.widget.Button;
    import junit.framework.Assert;
    
    public class StartActivityForResultTest extends ActivityInstrumentationTestCase2<StartActivityForResult> {
    
        public StartActivityForResultTest() {
            super(StartActivityForResult.class);
        }
    
        @Override
        public void setUp() throws Exception {
            super.setUp();
    
            this.setActivityInitialTouchMode(false);
    
            this.activity = this.getActivity();
            this.startButton = (Button) this.activity.findViewById(R.id.start_button);
        }
    
        @Override
        public void tearDown() throws Exception {
            this.activity.finish();
    
            super.tearDown();
        }
    
        @UiThreadTest
        public void testStartButtonOnClick() {
            Assert.assertTrue(this.startButton.performClick());
    
            Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
            Assert.assertNotNull(result);
    
            Instrumentation.ActivityMonitor am = new Instrumentation.ActivityMonitor(ChildActivity.class.getName(), result, true);
            Assert.assertNotNull(am);
    
            Activity childActivity = this.getInstrumentation().waitForMonitorWithTimeout(am, TIME_OUT);
            Assert.assertNotNull(childActivity);
    
            Assert.fail("How do I check that StartActivityForResult correctly handles the returned result?");
        }
        private Activity activity = null;
        private Button startButton = null;
        private static final int TIME_OUT = 5 * 1000; // 5 seconds
    }
    

    As you can see, I figured out how to mock up a result using Instrumentation.ActivityResult and Instrumentation.ActivityMonitor. How do I check that StartActivityForResult.onActivityResult() properly handles this result?

    • Code-Apprentice
      Code-Apprentice about 8 years
      @Vince Yes, I am aware of this. I personally prefer this particular usage since all of the assert*() methods are static.
  • Code-Apprentice
    Code-Apprentice over 11 years
    As you can see from my code, I know how to use onActivityResult() in my StartActivityForResult activity. My question is how to write a JUnit test to ensure that it behaves correctly.
  • Code-Apprentice
    Code-Apprentice over 11 years
    Thanks for the suggestions. My StartActivityForResult class in the original post is simply an example. In my real application, I have close to a dozen different Activities to test. Some of these activities return more than one piece of data. Your test code suggests that I have to modify the interface of each Activity just for testing by adding getters for each piece of data returned by the child activity. I don't mind the extra work of writing this code. I'm just not entirely satisfied with polluting the production code with code that is only used for testing.
  • yorkw
    yorkw over 11 years
    These are just samples for showing how to test onActivityResult() and do some basic assertion, depend on your demand, you don't need alter your Activity class in all cases, for example, if all your onActivityResult() does is update some UI stuff i.e. a TextView, you can directly check the the updated text without adding any instance variable and getter/setter methods.
  • Code-Apprentice
    Code-Apprentice over 11 years
    That's a good point. I imagine that most of the time onActivityResult() will modify the UI in some way, as you said.