使用Espresso在Android上测试进度条

时间:2016-02-03 20:20:22

标签: android android-espresso

工作流程应如下:

  1. 活动开始
  2. 进度条可见
  3. 网络请求触发(空闲资源已经注册,因此espresso知道如何等待它)。
  4. 隐藏进度条
  5. 显示来自网络的文字。
  6. 到目前为止,我已经为 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())));
    

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

    编辑:这也不起作用:

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

4 个答案:

答案 0 :(得分:13)

Espresso有动画问题。你可以将进度条的drawable设置为静态的,仅用于测试,它可以按预期工作。

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

答案 1 :(得分:5)

正如我所看到的,Espresso与跳过动态UI操作紧密相关,这就是您无法使用ProgressBar测试Espresso的原因。但是,您可以使用其他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'

答案 2 :(得分:4)

看起来这可能不是真的可能。虽然它是一个较旧的组发布,但在Android Test Kit Discussion中有一个相当决定性的答案,其中声明UI线程在进度条动画期间不会休息,因此Espresso框架无法执行。

Marcus Klepp建议通过使用构建类型来超越此here。 Gradle插件允许您定义不同的build types。您可以在androidTest构建类型中设置不同的布局,以使用通用的内容替换相关的View。如果你所做的只是在一组条件下确认小部件isDisplayed(),而在另一组条件下确认not(isDisplayed()),那么你肯定可以通过不同的布局文件来实现它。并不是说它不是一点点提升。

最后,此处可能还有其他帖子,其中包含一些其他信息:"java.lang.RuntimeException: Could not launch intent" for UI with indeterminate ProgressBar

答案 3 :(得分: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()));
}