仅当应用程序 100% 完成时,才能在仪器测试后正确清理/拆卸

tir*_*r38 8 android android-espresso

我有一堆端到端仪器测试(依赖于 Espresso),它们启动我们的启动器活动,然后在我们的应用程序中导航(最终创建多个活动)。在每次测试结束时,我们@After带注释的拆卸方法都会执行一些清理工作。

我们遇到的问题是,测试完成(成功或失败的断言)后,应用程序仍在“运行”,因此某些清理实际上导致应用程序崩溃。如果断言成功,这会导致误报,或者隐藏测试失败(我们只看到崩溃而不是失败的断言)。

这是一个例子:

import android.app.Instrumentation;
import android.content.Intent;
import android.preference.PreferenceManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;

import com.example.SplashActivity;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import static android.support.test.InstrumentationRegistry.getInstrumentation;

public class ExampleTest {

    @Rule
    public ActivityTestRule<SplashActivity> splashActivityTestRule
            = new ActivityTestRule<>(SplashActivity.class, true, false);

    Instrumentation.ActivityMonitor splashActivityMonitor;

    @Before
    public void setUp() {
        splashActivityMonitor = new Instrumentation.ActivityMonitor(SplashActivity.class.getName(), null, false);
        getInstrumentation().addMonitor(splashActivityMonitor);
    }

    @Test
    public void someTest() throws Exception {
        // ... other test-specific setup before starting splash activity

        // start first activity
        splashActivityTestRule.launchActivity(new Intent());

        // a bunch of espresso steps that result in several other activities
        // ... creating and adding Instrumentation.ActivityMonitor for each one

        // assert something
    }

    @After
    public void tearDown() {
        // clear shared prefs to prepare for next test
        PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext())
                .edit()
                .clear()
                .apply();

        // At this point the app is still running. Maybe a UI is still loading that was not relevant to the test, 
        // or some mock web request is in flight. But at some point after the final assert in our test, the app needs
        // to get something from shared prefs, which we just cleared, so the app crashes.
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,应用程序在拆卸方法期间仍在运行。我们在此处对应用程序状态所做的任何更改都可能会导致应用程序崩溃。

那么,在进行清理之前,我如何断言应用程序已死亡并消失呢?

我想出了一些可能的(但丑陋的)解决方案:

最终断言后,继续导航回应用程序中的某个中立点(即使用 espresso 注销并返回启动屏幕)。这应该可行,但它会为每个测试添加许多其他步骤。另外,我不确定如果断言失败这是否会起作用。

或者在拆卸中执行某种应用程序终止:

public void tearDown() {
    // finish all tasks before cleaning up
    ActivityManager activityManager =
            (ActivityManager) InstrumentationRegistry.getTargetContext().getSystemService(Context.ACTIVITY_SERVICE);

    List<ActivityManager.AppTask> appTasks = activityManager.getAppTasks();
    for (ActivityManager.AppTask appTask : appTasks) {
        appTask.finishAndRemoveTask();
    }

    // clear shared prefs to prepare for next test
    PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext())
            .edit()
            .clear()
            .apply();
}
Run Code Online (Sandbox Code Playgroud)

更新:

我知道我可以使用ActivityTestRule.afterActivityFinished() 文档,但我认为这不适用于多种活动。

den*_*nys 5

您所描述的问题可以通过使用来解决AndroidTestOrchestrator。来自Android官方文档:

使用AndroidJUnitRunner1.0 或更高版本时,您可以访问名为 Android Test Orchestrator 的工具,它允许您在自己的 Instrumentation 调用中运行每个应用程序的测试。

每次测试运行后,测试中的应用程序将被自动清理。

build.gradle启用的文件示例AndroidTestOrchestrator

  1. AndroidTestOrchestrator与 Android 支持库一起使用- github 链接
  2. AndroidTestOrchestrator与 AndroidX 测试库一起使用- github 链接

官方 Android 文档 -链接


Mar*_*ler 1

a) 人们可以设置默认首选项而不是将其清空 -

b) 或使用共享资源本身,以防止竞争条件:

public static final String PREFERENCE_KEY_TEST_COUNT = "testCount";
public static final int MAX_TEST_COUNT = 6;

@After
public void tearDown() {
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getTargetContext()):
    if(prefs.getPreference(PREFERENCE_KEY_TEST_COUNT, 0) >= MAX_TEST_COUNT) {
        prefs.edit().clear().apply();
    } else {
        int testCount = prefs.getPreference(PREFERENCE_KEY_TEST_COUNT, 0) + 1;
        prefs.edit().putInt(PREFERENCE_KEY_TEST_COUNT, testCount).apply();
    }
}
Run Code Online (Sandbox Code Playgroud)

c) 或测试套件和自定义Runner可用于控制测试;这个例子解释了这一点。