使用检测和JUnit4测试重新创建Android Activity

Mat*_*szL 9 java instrumentation android automated-tests android-junit

我想为重新创建活动编写测试.执行旋转是可选的.

我希望测试能够被谷歌"祝福"的测试框架的最新版本编写.我是编写测试的新手,所以我想学习基本的,主流的,支持良好的工具.当我掌握基础知识时,任何第三方测试框架都会没问题.而且由于我想测试非常基本的,经常出现的场景,基本工具应该足够了,对吧?

最小测试代码:

public class MainActivity extends AppCompatActivity {

    static int creationCounter = 0;
    Integer instanceId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ++creationCounter;
        instanceId = new Integer(creationCounter);
        Log.d("TEST", "creating activity " + this + ", has id " + instanceId);
    }
}
Run Code Online (Sandbox Code Playgroud)

和测试类:

@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {

    @Rule
    public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void useAppContext() throws Exception {

        MainActivity activity1 = mActivityTestRule.getActivity();
        int act1 = activity1.instanceId.intValue();
        int counter1 = MainActivity.creationCounter;
        assertEquals(1, counter1);
        assertEquals(1, act1);


        Log.d("TEST", "requesting rotation");
        // method 1
         activity1.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        // method 2 //https://gist.github.com/nbarraille/03e8910dc1d415ed9740#file-orientationchangeaction-java
        // onView(isRoot()).perform(orientationLandscape());

        getInstrumentation().waitForIdleSync(); // I thought this should suffice
        // How to do this?
        //somehowRefreshActivityInstanceInsideTestRule();

        MainActivity activity2 = mActivityTestRule.getActivity();
        int act2 = activity2.instanceId.intValue();
        int counter2 = MainActivity.creationCounter;
        Log.d("TEST", "newly acquired activity " + activity2 + " has id " + act2 + "/" + counter2);

        assertEquals(2, counter2);
        assertEquals(2, act2);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码(方法1或2)给出了logcat:

D/ActivityTestRule: Launching activity example.com.rotationtest.MainActivity
D/TEST: creating activity example.com.rotationtest.MainActivity@47404a3, has id 1
D/TEST: requesting rotation
D/TEST: creating activity example.com.rotationtest.MainActivity@169887e, has id 2
D/TEST: newly acquired activity example.com.rotationtest.MainActivity@47404a3 has id 1/2
I/TestRunner: failed: useAppContext(example.com.rotationtest.ExampleInstrumentedTest)
I/TestRunner: ----- begin exception -----
I/TestRunner: java.lang.AssertionError: expected:<2> but was:<1>
Run Code Online (Sandbox Code Playgroud)

我的诊断,如果我错了,请纠正我:

  1. activity1.setRequestedOrientation导致在其他线程中创建新活动.我希望它会得到适当的捆绑
  2. .getInstrumentation()waitForIdleSync(); 导致test等待,直到创建新活动
  3. mActivityTestRule.getActivity(); 仍然返回旧的活动实例.
  4. 我需要一些方法来刷新测试规则中保存的活动实例,释放之前保存的实例.

我找到了旧版测试框架的答案:Android的仪器测试 - 如何在方向更改后接收新的活动?

mActivity.setRequestedOrientation(
    ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mActivity.finish();
setActivity(null);
mActivity = getActivity();
getInstrumentation().waitForIdleSync();
Run Code Online (Sandbox Code Playgroud)

但我不知道如何将其翻译成新版本.

编辑:

上述两种方法都将活动保留在销毁状态:assertFalse(mActivityTestRule.getActivity().isDestroyed()); 失败.

我找到了另一种方法(Destroy并重启Activity with Testing Support Library),它重新创建活动实例,但不通过onSaveInstanceState保持其状态

Mat*_*szL 1

我终于在这里找到了工作解决方案:Get Current Activity in Espresso android

适应我的需求后代码如下所示:

public class CurrentActivityTestRule<T extends Activity> extends ActivityTestRule<T> {
    public CurrentActivityTestRule(Class<T> activityClass) {
        super(activityClass, false);
    }

    public CurrentActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
        super(activityClass, initialTouchMode, true);
    }

    public CurrentActivityTestRule(Class<T> activityClass, boolean initialTouchMode, boolean launchActivity) {
        super(activityClass, initialTouchMode, launchActivity);
    }

    public T getCurrentActivity() {
        getInstrumentation().waitForIdleSync();
        final Activity[] activity = new Activity[1];
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run() {
                java.util.Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
                activity[0] = Iterables.getOnlyElement(activities);
            }});
        T current = (T) activity[0];
        return current;
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样使用:

onView(isRoot()).perform(orientationLandscape());
Activity oldActivityInstance = mActivityTestRule.getActivity();
Activity currentActivityInstance = mActivityTestRule.getCurrentActivity();
Run Code Online (Sandbox Code Playgroud)

我有这个与库版本一起使用:

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile('com.android.support.test:runner:0.5', {
    exclude group: 'com.android.support', module: 'support-annotations'
})
Run Code Online (Sandbox Code Playgroud)