仅当应用程序100%完成后,才能在工具测试后正确清理/拆除

时间:2019-01-07 17:58:00

标签: 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.
    }
}

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

那么我怎么能断言应用程序已经死了并且在进行清理之前

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

在最终断言之后,继续导航回应用程序中的某个中性点(即使用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();
}

更新:

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

2 个答案:

答案 0 :(得分:1)

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

  

使用AndroidJUnitRunner 1.0或更高版本时,您可以访问   到名为Android Test Orchestrator的工具,该工具可让您运行   您的应用程序的每个测试都在其自己的“ Instrumentation”调用中进行。

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

启用了build.gradle

AndroidTestOrchestrator文件示例:

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

Android官方文档-link

答案 1 :(得分:0)

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();
    }
}

c)或Test Suite和自定义Runner可以用来控制测试; example对此进行了解释。