Espresso不会等到对话框显示失败

时间:2017-03-13 14:50:29

标签: android android-espresso

我正在使用Espresso进行一些简单的测试。其中一个是关于点击视图并检查是否显示了对话框。

我的问题是有时是有效的,有时则不然。它只在我检查对话框之前放置 sleep 时才会起作用。没有解决睡眠的任何解决方案?

这是我的代码(如此简单):

onView(withId(R.id.forgot_password)).perform(click());
// only works if use Thread.sleep(ms) here
onView(withText(R.string.reset_password)).check(matches(isDisplayed()));

编辑:

我正在使用静态助手显示对话框,但简化就是这样。而且我没有在中间执行任何后台任务。

final TextInputDialog textInputDialog = new

TextInputDialog.Builder(context)
                .setTitle(titleId)
                .setInputType(inputType)
                .setHint(hintId)
                .setPreFilledText(preFilledText)
                .setNegativeButton(R.string.cancel, null)
                .setPositiveButton(positiveButtonId, onTextSubmittedListener)
                .create();
textInputDialog.show(textInputDialog);

谢谢!

3 个答案:

答案 0 :(得分:3)

最后似乎问题是动画。要使make Espresso正常工作,需要在开发人员选项菜单中禁用动画。

Disable animations

在这种情况下,问题得以解决。但是在其他情况下,问题可能是一个后台任务,就像我的问题的评论所暗示的那样。所以我建议您查看 IdlingResource https://medium.com/azimolabs/wait-for-it-idlingresource-and-conditionwatcher-602055f32356#.pw55uipfj或此Espresso: Thread.sleep( );

答案 1 :(得分:1)

在某些情况下,禁用动画是不可能的:

  • 在云设备上运行测试时。 Firebase testlab不允许更改设备配置
  • 当您等待某个后台线程更新用户界面时,例如http响应。

在这些情况下,最简单的是等待元素显示。方法如下:

简单助手

class WaifForUIUpdate {

public static void waifForWithId(@IdRes int stringId) {

    ViewInteraction element;
    do {
        waitFor(500);

        //simple example using withText Matcher.
        element = onView(withText(stringId);

    } while (!MatcherExtension.exists(element));

}

static void waitFor(int ms) {
    final CountDownLatch signal = new CountDownLatch(1);

    try {
        signal.await(ms, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        Assert.fail(e.getMessage());
    }
}
}
来自twisterrob

匹配

public class MatcherExtension {
 @CheckResult
    public static boolean exists(ViewInteraction interaction) {
        try {
            interaction.perform(new ViewAction() {
                @Override
                public Matcher<View> getConstraints() {
                    return any(View.class);
                }

                @Override
                public String getDescription() {
                    return "check for existence";
                }

                @Override
                public void perform(UiController uiController, View view) {
                    // no op, if this is run, then the execution will continue after .perform(...)
                }
            });
            return true;
        } catch (AmbiguousViewMatcherException ex) {
            // if there's any interaction later with the same matcher, that'll fail anyway
            return true; // we found more than one
        } catch (NoMatchingViewException ex) {
            return false;
        } catch (NoMatchingRootException ex) {
            // optional depending on what you think "exists" means
            return false;
        }
    }

}

<强>用法

WaifForUIUpdate.waifForWithId(R.string.some_string);
//now do your validations

答案 2 :(得分:0)

我最终使用了不同于我的第一个答案的另一种方法,该方法也很有效,但是通过使用CountdownLatch可以更轻松地适应任何情况。 这是代码:

public static boolean viewExists(final Matcher<View> viewMatcher, final long millis) throws InterruptedException {
    final Boolean[] found = new Boolean[1];

    final CountDownLatch latch = new CountDownLatch(1);
    ViewAction action = new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewMatcher.toString() + "> during " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + millis;


            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {

                    if (viewMatcher.matches(child)) {
                        Log.d(TAG, "perform: found match");
                        found[0] = true;
                        latch.countDown();
                        return;
                    }
                }

                uiController.loopMainThreadForAtLeast(50);
            }
            while (System.currentTimeMillis() < endTime);

            found[0] = false;
            latch.countDown();
        }
    };
    onView(isRoot()).perform(action);

    latch.await();
    return found[0];
}

Google的方法是使用Idling resource类,但需要将测试代码插入生产apk,或使用风味和依赖注入模式来避免这种情况。