【问题标题】:Get current Activity on Android Instrumentation Test在 Android Instrumentation 测试中获取当前活动
【发布时间】:2014-06-06 05:23:25
【问题描述】:

我的 Android 应用程序上的 MainActivity 会检查用户是否已登录(存储在 SharedPreferences 中)以及是否没有将用户带到 LoginActivity。我正在尝试使用以下代码对此进行测试

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {

private static final int TIME_OUT = 5000; /* miliseconds */

private MainActivity mMainActivity;
private Instrumentation mInstrumentation;
private SharedPreferences mLoginPrefs;

public MainActivityTest() {
    super(MainActivity.class);
}

protected void setUp() throws Exception {
    super.setUp();

    setActivityInitialTouchMode(false);

    mMainActivity = getActivity();
    mInstrumentation = getInstrumentation();
    mLoginPrefs = mInstrumentation.getTargetContext().getSharedPreferences(LoginActivity.PREFS_NAME, Context.MODE_PRIVATE);

    SharedPreferences.Editor editor = mLoginPrefs.edit();
            // User is not logged in, so it should be redirect to LoginActivity
    editor.putBoolean("logged_in", false);
    editor.commit();
}

//...

public void testC_OpenLoginActivityIfUserIsNotLoggedIn() {
    ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
    Activity nextActivity = mInstrumentation.waitForMonitorWithTimeout(monitor, TIME_OUT);

    assertNotNull(nextActivity);
    nextActivity.finish();

    SharedPreferences.Editor editor = mLoginPrefs.edit();
            // Login the user so we can continue the tests
    editor.putBoolean("logged_in", true);
    editor.commit();
}

但这不起作用,LoginActivity 打开但 waitForMonitorWithTimeout 永远不会返回,所以我卡在 LoginActivity 上(我需要回到 MainActivity 进行其他测试)。

类似于SO Question 的代码适用于按钮点击,但任何点击都不会加载此活动,所以我想可能没有时间让监视器工作。

我只需要一种方法来获取实际的 Activity,这样我就可以进行断言并使其完成以继续我的测试。

还有一件事:如果可能的话,我更喜欢不使用 Robotium 的方法。

【问题讨论】:

    标签: java android automated-tests junit3


    【解决方案1】:

    你在正确的轨道上。你需要调用的方法是:

    monitor.waitForActivityWithTimeout(TIME_OUT);
    

    而不是

    mInstrumentation.waitForMonitorWithTimeout(monitor, TIME_OUT);
    

    【讨论】:

    • 根据API参考,他做的是对的:waitForMonitorWithTimeout(Instrumentation.ActivityMonitor monitor, long timeOut)
    【解决方案2】:

    为了解决你的问题,先看看你的测试最重要的两个方法:

    Instrumentation#addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean)
    Instrumentation.ActivityMonitor#waitForActivity()
    

    根据 Android API 参考:

    添加监视器

    添加一个新的 Instrumentation.ActivityMonitor,将在活动启动时进行检查。在任何现有监视器之后添加监视器;仅当现有监视器都不能自己处理 Intent 时,才会命中监视器。

    等待活动

    阻塞,直到创建一个 匹配此监视器的 Activity,返回生成的 Activity。


    现在让我们更清楚一点。

    addMonitor 应始终预期活动开始之前调用,永远不会太晚。

    waitForActivity 应该仅在预期活动开始之后调用,永远不要太早,因为它会阻塞。


    回到你的代码:

    你把他们两个一起召唤,中间没有任何魔法发生。因此,对于 addMonitor 来说太晚了,或者对于 waitForActivity 来说太早了。

    ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
    Activity nextActivity = mInstrumentation.waitForMonitorWithTimeout(monitor, TIME_OUT);
    

    如果调用 waitForActivity 为时过早,它将阻塞并失败,直到超时(因为预期的活动尚未命中),并且您永远不会看到预期的活动正在启动。

    如果调用 addMonitor 来不及,则在启动预期的 Activity 后开始监视,此后预期的 Activity 不再启动,因此 waitForActivity 会因为没有命中监视器而阻塞。

    所以这两种情况的区别预期的活动是否开始。对于您的情况,我认为调用 addMonitor 为时已晚。

    解决方案非常简单:只需在 LoginActivity 开始之前将 addMonitor 移动到足够早的位置,也许将其移动到 setUp 方法,如下所示:

    mInstrumentation = getInstrumentation();
    ActivityMonitor monitor = mInstrumentation.addMonitor(LoginActivity.class.getName(), null, false);
    

    顺便说一句,对于您的情况,超时或不超时无关紧要。

    不要忘记删除不再需要的monitor,例如:

    @Override
    protected void tearDown() throws Exception {
        mInstrumentation.removeMonitor(monitor);
        super.tearDown();
    }
    

    【讨论】:

    • 在 Kotlin 中,过去到 addMonitor() 的类名应该是什么?我尝试了"com.locuslabs.sdktestapp.MapActivity"MapActivity::class.java.name.toString()MapActivity::class.java.canonicalName.toString() 等,但waitForMonitorWithTimeout() 全部返回了null
    【解决方案3】:
    public static Activity getCurrentActivity() {
        final Activity[] currentActivity = {null};
        getInstrumentation().runOnMainSync(new Runnable() {
            public void run() {
                Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance()
                        .getActivitiesInStage(RESUMED);
                if (resumedActivities.iterator().hasNext()) {
                    currentActivity[0] = (Activity) resumedActivities.iterator().next();
                }
            }
        });
        return currentActivity[0];
    }
    

    【讨论】:

    • 这个解决方案对我来说适用于 Kotlin 应用程序,谢谢。
    • 出于某种原因,.getActivitiesInStage(RESUMED) 只给了我一个空集合。这仅适用于检测同一包中的活动吗?
    猜你喜欢
    • 2014-08-22
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    • 2018-08-25
    • 2022-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多