Instrumentation test for Android - How to receive

2019-02-14 04:03发布


I'm trying to test, if newly created Activity (after orientation change) is properly reinitialized. The code below shows that activity returned from getActivity() is the one constructed in setUp(), not the newly created one.

Test:

public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity>{
 private static final String TAG = "RAMPS";
 private MyActivity mActivity;

 public MyActivityTest() {
    super("com.ramps", MyActivity.class);       
 }

 protected void setUp() throws Exception {
     super.setUp();
     mActivity = getActivity();
     Log.v(TAG, "setUp; activity=" + mActivity);
 }

public void testOrienationChange(){
     mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
     getInstrumentation().waitForIdleSync();
     MyActivity newActivity = getActivity(); //should be new, but it's not
     Log.v(TAG, "testOrienationChange; activity=" + newActivity);       
 }
}


Activiy:

public class MyActivity extends Activity {
    private static final String TAG = "RAMPS";  

 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.v(TAG, "onCreate; activity=" + this);
    setContentView(new TextView(this));     
 }
    //...rest of stuff like onSaveInstanceState() etc.
}


And logs:

06-11 14:16:52.431: V/RAMPS(367): onCreate; activity=MyActivity@44eb8690
06-11 14:16:52.891: V/RAMPS(367): setUp; activity=MyActivity@44eb8690
06-11 14:16:52.971: V/RAMPS(367): onCreate; activity=MyActivity@44ee5178
06-11 14:16:53.131: V/RAMPS(367): testOrienationChange; activity=MyActivity@44eb8690


As mentioned before, logs shows that new activity is indeed created (MyActivity@44ee5178), but getActivity() return the old activity, created in setUp() (MyActivity@44eb8690). Is it possible to access newly created one?

5条回答
冷血范
2楼-- · 2019-02-14 04:06

I've had the same problem in my UI tests. I don't use Robotium, but I created a new base test class with getActivity() that always returns current activity. The idea is to add an ActivityMonitor before orientation change and just update current activity on monitor wait:

public class UiTest<T extends Activity> extends ActivityInstrumentationTestCase2<T> {

private final Class<T> activityClass;
protected T activity;

public UiTest(Class<T> activityClass) {
  super(activityClass);
  this.activityClass = activityClass;
}

@Override
protected void setUp() throws Exception {
  super.setUp();
  setActivityInitialTouchMode(false);  // Depends on your needs.
  activity = super.getActivity();
  activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

/**
 * Rotates the test device and updates current activity.
 */
protected final void rotate() {
  int nextOrientation =
      activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
          ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
          : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
  Instrumentation.ActivityMonitor monitor =
      new Instrumentation.ActivityMonitor(activityClass.getName(), null, false);
  getInstrumentation().addMonitor(monitor);
  activity.setRequestedOrientation(nextOrientation);
  getInstrumentation().waitForIdleSync();
  this.activity = (T) getInstrumentation().waitForMonitor(monitor);
}

@Override
public T getActivity() {
  return activity;
}

Of course this won't work if activity class changes during the test or if orientation changes by other means, not with rotate() method. I hope this helps somebody.

查看更多
【Aperson】
3楼-- · 2019-02-14 04:13

Took @Smok's answer and updated it to always rotate:

@SuppressWarnings("unchecked") // it's fine
public static <T extends Activity> T rotate(ActivityInstrumentationTestCase2<T> testCase) {
    T activity = testCase.getActivity();
    int orientation = activity.getWindowManager().getDefaultDisplay().getRotation();
    int nextOrientation = orientation == Configuration.ORIENTATION_LANDSCAPE ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
    Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(activity.getClass().getName(), null, false);
    testCase.getInstrumentation().addMonitor(monitor);
    activity.setRequestedOrientation(nextOrientation);
    testCase.getInstrumentation().waitForIdleSync();
    return (T) testCase.getInstrumentation().waitForMonitor(monitor);
}

This fixes the problem of the orientation returning UNSPECIFIED

查看更多
太酷不给撩
4楼-- · 2019-02-14 04:20

Ok, I think I finally managed to solve it - with robotium framework. I'm attaching the solution in case someone had the same problem.

Test:

public class MyActivityTest extends ActivityInstrumentationTestCase2<MyActivity>{

private static final String TAG = "RAMPS";
private MyActivity mActivity;
private Solo mSolo;

public MyActivityTest() {
    super("com.ramps", MyActivity.class);

}

@Override
protected void setUp() throws Exception {
    super.setUp();
    mActivity = getActivity();
    mSolo = new Solo(getInstrumentation(), getActivity());
    Log.v(TAG, "setUp; activity=" + mActivity);
}

public void testOrienationChange(){     
    mSolo.setActivityOrientation(Solo.LANDSCAPE);
    getInstrumentation().waitForIdleSync();
    MyActivity newActivity = getActivity(); //should be new, but it's not
    Activity newActivity2 = mSolo.getCurrentActivity(); //this will return new activity
    Log.v(TAG, "testOrienationChange; activity=" + newActivity);
    Log.v(TAG, "testOrienationChange; activity2=" + newActivity2);
}   

}

And log messages - for confirmation:

06-11 18:47:02.631: V/RAMPS(716): onCreate; activity=MyActivity@44c326a8
06-11 18:47:03.061: V/RAMPS(716): setUp; activity=MyActivity@44c326a8
06-11 18:47:03.781: V/RAMPS(716): onCreate; activity=MyActivity@44c481e0
06-11 18:47:04.482: V/RAMPS(716): testOrienationChange; activity=MyActivity@44c326a8
06-11 18:47:04.482: V/RAMPS(716): testOrienationChange; activity2=MyActivity@44c481e0

As you can see, activity returned from mSolo.getCurrentActivity() is the same that was created after orientation change. I really recommend Robotium - another great piece of code from Jayway!

查看更多
狗以群分
5楼-- · 2019-02-14 04:25

Instead of

 MyActivity newActivity = getActivity(); //should be new, but it's not

Try

MyActivity newActivity = instrumentation.startActivitySync(intent);

I think this method maybe help you

查看更多
Deceive 欺骗
6楼-- · 2019-02-14 04:28

This solved my issue, using only the Android testing framework:

    mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    mActivity.finish();
    setActivity(null);
    mActivity = getActivity();
    getInstrumentation().waitForIdleSync();

AFAIK, getActivity() does create a new activity if the current one is null which is done by setting it with setActivity(null). Also, the new activity will have the orientation set by Activity.setRequestedOrientation(int).

If you need to check whether the state is being correctly saved when orientation changes, you should call getInstrumentation().callActivityOnCreate(mActivity, Bundle) from the UI thread.

查看更多
登录 后发表回答