Espresso:Thread.sleep();

Cha*_*ham 91 testing android android-espresso

Espresso声称没有必要Thread.sleep();,但我的代码不起作用,除非我包含它.我正在连接到IP.连接时,会显示进度对话框.我需要sleep等待对话框解散.这是我使用它的测试片段:

    IP.enterIP(); // fills out an IP dialog (this is done with espresso)

    //progress dialog is now shown
    Thread.sleep(1500);

    onView(withId(R.id.button).perform(click());
Run Code Online (Sandbox Code Playgroud)

我曾尝试使用使用此代码,Thread.sleep();但它说不R.id.Button存在.我能让它发挥作用的唯一方法就是睡觉.

此外,我尝试用Thread.sleep();类似的东西替换,getInstrumentation().waitForIdleSync();但仍然没有运气.

这是唯一的方法吗?或者我错过了什么?

提前致谢.

Ole*_*nko 105

在我看来,正确的方法是:

/** Perform action of waiting for a specific view id. */
public static ViewAction waitId(final int viewId, final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewId + "> 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;
            final Matcher<View> viewMatcher = withId(viewId);

            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                    // found view with required ID
                    if (viewMatcher.matches(child)) {
                        return;
                    }
                }

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

            // timeout happens
            throw new PerformException.Builder()
                    .withActionDescription(this.getDescription())
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new TimeoutException())
                    .build();
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

然后使用模式将是:

// wait during 15 seconds for a view
onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15)));
Run Code Online (Sandbox Code Playgroud)

  • 我希望您了解它的样本,您可以根据自己的需要进行复制/粘贴和修改.您完全有责任在自己的业务需求中使用它,而不是我的. (4认同)
  • 谢谢Alex,你为什么选择这个选项而不是IdlingResource或AsyncTasks? (2认同)

Hes*_*sam 42

感谢AlexK的精彩回答.有些情况下你需要在代码中做一些延迟.它不一定等待服务器响应,但可能正在等待动画完成.我个人有Espresso idolingResources的问题(我想我们正在为一个简单的事情编写许多代码行)所以我改变了AlexK对以下代码的处理方式:

/**
 * Perform action of waiting for a specific time.
 */
public static ViewAction waitFor(final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "Wait for " + millis + " milliseconds.";
        }

        @Override
        public void perform(UiController uiController, final View view) {
            uiController.loopMainThreadForAtLeast(millis);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以创建一个Delay类并将此方法放入其中,以便轻松访问它.您可以在Test类中以相同的方式使用它:onView(isRoot()).perform(waitFor(5000));

  • perform方法甚至可以用这样的一行简化:uiController.loopMainThreadForAtLeast(millis); (6认同)
  • 我不明白调用这个“ViewAction”与调用“SystemClock.sleep(millis)”有什么不同。两者都在返回之前等待固定的毫秒数。我强烈建议定义您的“ViewAction”类以等待特定条件(如[此处](/sf/answers/1579430821/)和[此处](https://stackoverflow.com/所示) a/64497518/1071320)) 因此在大多数情况下它们返回得更快,并且在出现错误时仅等待最大毫秒数。 (2认同)

Mat*_*att 21

我在寻找类似问题的答案时偶然发现了这个问题,我在等待服务器响应并根据响应更改元素的可见性.

虽然上面的解决方案肯定有帮助,但我最终从chiuki找到了这个优秀的例子,现在每当我等待应用程序闲置期间的操作时,我都会使用这种方法.

我已经将ElapsedTimeIdlingResource()添加到我自己的实用程序类中,现在可以有效地将其用作Espresso适当的替代方案,现在使用非常简洁:

// Make sure Espresso does not time out
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);

// Now we wait
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);

// Stop and verify
onView(withId(R.id.toggle_button))
    .check(matches(withText(R.string.stop)))
    .perform(click());
onView(withId(R.id.result))
    .check(matches(withText(success ? R.string.success: R.string.failure)));

// Clean up
Espresso.unregisterIdlingResources(idlingResource);
Run Code Online (Sandbox Code Playgroud)

  • 如果您需要更改空闲策略,您可能没有正确实现空闲资源。从长远来看,最好花时间解决这个问题。这种方法最终会导致缓慢和不稳定的测试。查看 https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/ (2认同)

Cab*_*zas 15

我认为添加这一行更容易:

SystemClock.sleep(1500);
Run Code Online (Sandbox Code Playgroud)

在返回之前等待给定的毫秒数(uptimeMillis).类似于sleep(long),但不会抛出InterruptedException; interrupt()事件被推迟到下一个可中断操作.在至少经过指定的毫秒数后才返回.


Dav*_*edy 8

这与此答案类似,但使用超时而不是尝试,并且可以与其他 ViewInteractions 链接:

/**
 * Wait for view to be visible
 */
fun ViewInteraction.waitUntilVisible(timeout: Long): ViewInteraction {
    val startTime = System.currentTimeMillis()
    val endTime = startTime + timeout

    do {
        try {
            check(matches(isDisplayed()))
            return this
        } catch (e: NoMatchingViewException) {
            Thread.sleep(50)
        }
    } while (System.currentTimeMillis() < endTime)

    throw TimeoutException()
}
Run Code Online (Sandbox Code Playgroud)

用法:

onView(withId(R.id.whatever))
    .waitUntilVisible(5000)
    .perform(click())
Run Code Online (Sandbox Code Playgroud)

  • 我使用了这种方法,但它对我来说并不完全有效。我必须捕获 AssertionFailedError 而不是 NoMatchingViewException。有了这个改变,它就完美地工作了 (2认同)

Roc*_*nat 7

您可以使用Barista方法:

BaristaSleepActions.sleep(2000);

BaristaSleepActions.sleep(2, SECONDS);

Barista是一个包装Espresso的库,以避免添加所接受答案所需的所有代码.这是一个链接! https://github.com/SchibstedSpain/Barista

  • 我不明白这和仅仅进行线程睡眠有什么区别 (2认同)