【问题标题】:Espresso does not wait until dialog is shown and failEspresso 不会等到显示对话框并失败
【发布时间】:2020-10-08 16:38:54
【问题描述】:

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

我的问题是有时有效,有时无效。只有当我在检查对话框之前放置一个 sleep 时,它才会始终有效。任何不使用 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()));

编辑:

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

最终的 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);

谢谢!

【问题讨论】:

  • 我不明白为什么睡眠不是一个好的解决方案。您想等待一段时间,而这正是 sleep 的作用。
  • 该点击事件是立即显示对话框,还是执行一些后台作业然后显示对话框?
  • thread.sleep 在 android espresso 测试中不是一个好的解决方案。通常它不应该是必要的,espresso 等到主线程完成 - 所以它应该等待对话框。你能分享一下你是如何显示对话框的吗?
  • 我已经更新了我的答案。我只是检查一些变量和编辑文本并显示对话框。
  • 您提供的代码看起来不错,因此错误必须在其他地方。也许在您确切调用对话框的位置和方式显示。如果测试失败,还有什么错误消息?它失败/成功的频率如何?

标签: android android-espresso


【解决方案1】:

在某些情况下无法禁用动画,例如:

  • 在云设备上运行测试时。 Firebase 测试实验室不允许更改设备配置
  • 当您等待某个后台线程更新用户界面时,例如 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

【讨论】:

  • 这在元素存在的情况下工作,我如何打破语句,尝试 50 秒后..如果元素不存在..它不会退出 do while 循环..你能帮我解决上述问题吗
  • 我添加了第二个答案。这个很容易变成你需要的东西。
【解决方案2】:

最后,问题似乎出在动画上。为了使 Espresso 正常工作,需要在开发者选项菜单中禁用动画。

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

【讨论】:

    【解决方案3】:

    我最终使用了与我的第一个答案不同的另一种方法,它也很有效,但通过使用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,或者使用风味和依赖注入模式来避免它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多