使用Espresso在Android上测试进度条

20

工作流应该如下所示:

  1. 活动开始
  2. 进度条可见
  3. 网络请求启动(空闲资源已注册,因此Espresso知道如何等待它)。
  4. 隐藏进度条
  5. 显示来自网络的文本。

到目前为止,我已经为步骤1、3、5编写了断言,并且它完美地工作:

onView(withText("foo 1"))
    .check(matches(isDisplayed()));

问题是,我不知道如何让Espresso在请求发出之前之后验证进度条的可见性。

考虑以下onCreate()方法:

super.onCreate(...);
setContentView(...);

showProgressBar(true);
apiClient.getStuff(new Callback() {
    public void onSuccess() {
        showProgressBar(false);
    }
});

我已经尝试了以下方法,但它没有起作用:

// Activity is launched at this point.
activityRule.launchActivity(new Intent());

// Up to this point, the request has been fired and response was 
// returned, so the progress bar is now GONE.
onView(withId(R.id.progress_bar))
   .check(matches(isDisplayed()));

onView(withId(R.id.progress_bar))
    .check(matches(not(isDisplayed())));

这种情况发生的原因是,由于客户端被注册为空闲资源,espresso将等待它再次空闲之前运行第一个onView(...progressbar...)...,所以我需要一种方法让espresso知道在进入空闲状态之前运行它。

编辑:这也不起作用:

idlingResource.registerIdleTransitionCallback(new IdlingResource.ResourceCallback() {
        @Override
        public void onTransitionToIdle() {
            onView(withId(R.id.progress_bar))
                    .check(matches(isDisplayed()));
        }
    });

你需要让你的活动可测试。如果你想继续运行集成测试,你需要模拟/存根你的API调用。 - David Medenjak
1
API已经被存根并注册为一个空闲资源了。但这不是我要问的。 - Christopher Francisco
@ChristopherFrancisco 你是如何解决这个问题的?我正在测试一个几乎相似的场景,其中我按下登录按钮,异步任务显示一个对话框,我想测试它。但是espresso在异步任务完成之前不允许我执行任何操作。 - Shivam Pokhriyal
你解决了这个问题吗?我有一个类似的问题。请在此处检查我的解决方案尝试:https://stackoverflow.com/questions/70830477/test-runs-forever-when-using-espressos-onview-in-mockito-doanswer - enapi
4个回答

14

意大利浓缩咖啡在动画方面存在问题。您可以将进度条的可绘制对象设置为某些静态内容,仅供测试使用,这样它就像预期的那样工作。

Drawable notAnimatedDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.whatever);
((ProgressBar) getActivity().findViewById(R.id.progress_bar)).setIndeterminateDrawable(notAnimatedDrawable);

onView(withId(R.id.progress_bar)).check(matches(isDisplayed()));

您可以在“设置”->“开发者选项”->“窗口动画缩放”/“转换动画缩放”/“动画器持续时间缩放”中关闭动画。 - heloisasim
@heloisasim很遗憾没有提供帮助。 - Tim Kist
2
禁用动画似乎可以解决Android 7上的问题,但在Android 6及更早版本上无效。 - Michael

5

我看到Espresso非常紧密地跳过了所有动态UI操作,因此你无法使用Espresso测试ProgressBar。然而,你可以使用另一个Android Google工具UiAutomator来轻松完成此操作,如下所示:

    saveButton().click(); // perform action opening ProgressBar with UiAutomator, not Espresso
    assertTrue(progressBar().exists());

使用这些静态工具:
public static UiObject progressBar() {
    return uiObjectWithText(R.string.my_progress);
}

public static UiObject saveButton() {
    return uiObjectWithId(R.id.my_save_button);
}

public static UiObject uiObjectWithId(@IdRes int id) {
    String resourceId = getTargetContext().getResources().getResourceName(id);
    UiSelector selector = new UiSelector().resourceId(resourceId);
    return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}

public static UiObject uiObjectWithText(@StringRes int stringRes) {
    UiSelector selector = new UiSelector().text(getTargetContext().getString(stringRes));
    return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}

请确保您的build.gradle文件中包含以下内容:

androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'

您的解决方案对我无效,这是我的问题。https://stackoverflow.com/questions/54320660/espresso-testing-call-view-button-click-waiting-after-call-api-response-data-pro 请帮帮我,先生。 - Htut

4
看起来这可能不是真正可能的。虽然这是一个较旧的群组帖子,但在Android测试套件讨论中有一个相当决定性的答案,其中指出UI线程在进度条动画期间不休眠,因此Espresso框架无法执行。
Marcus Klepp建议通过使用构建类型来解决此问题这里。Gradle插件将允许您定义不同的构建类型。您可以在androidTest构建类型中设置不同的布局,用一些通用的东西替换相关的View。如果你只是确认小部件在一组条件下isDisplayed(),而在另一组条件下not(isDisplayed()),那么你肯定可以通过不同的布局文件实现。请注意,这需要一些努力。
最后,这里可能有另一个帖子,其中包含一些额外的信息:"java.lang.RuntimeException: Could not launch intent" for UI with indeterminate ProgressBar

0
在我的情况下,上面提供的解决方案也可以工作,但我进行了简化,因此添加了build.gradle uiautomator库。
androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.3'

并创建了一个新类,该类将与进度条等组件一起使用

public class ProgressBarHandler {

public static void waitUntilGoneProgressBar() {
    progressBar().waitUntilGone(10000);
}

private static UiObject progressBar() {
    return uiObjectWithId(R.id.progress_bar);
}

private static UiObject uiObjectWithId(@IdRes int id) {
    String resourceId = getTargetContext().getResources().getResourceName(id);
    UiSelector selector = new UiSelector().resourceId(resourceId);
    return UiDevice.getInstance(getInstrumentation()).findObject(selector);
}

}

在我的测试中,我使用了所有的Espresso方法,并且只有在需要时才在测试中使用UiAutomator,例如

public class LoginTest extends AbstractTest {

@Rule
public ActivityTestRule<LoginActivity> createAccountActivityTestRule = new ActivityTestRule<>(LoginActivity.class);

@Test
public void loginTest() {
    onView(withId(R.id.login_email)).perform(typeText("autotest666@gmail.com"));
    onView(withId(R.id.input_password)).perform(typeText("Password123."));
    onView(withId(R.id.login_log_in)).perform(click());
    waitUntilGoneProgressBar();
    onView(withId(R.id.fragment_home_title)).check(matches(isDisplayed()));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接