Android 浓缩咖啡:Thread.sleep();
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21417954/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Espresso: Thread.sleep( );
提问by Chad Bingham
Espresso claims that there is no need for Thread.sleep();
, but my code doesn't work unless I include it. I am connecting to an IP. While connecting, a progress dialog is shown. I need a sleep
to wait for the dialog to dismiss. This is my test snippet where I use it:
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());
I have tried this code withand withoutthe Thread.sleep();
but it says R.id.Button
doesn't exist. The only way I can get it to work is with sleep.
我已经尝试过使用和不使用此代码,Thread.sleep();
但它说不R.id.Button
存在。我让它工作的唯一方法就是睡觉。
Also, I have tried replacing Thread.sleep();
with things like getInstrumentation().waitForIdleSync();
and still no luck.
另外,我尝试用Thread.sleep();
类似的东西替换,getInstrumentation().waitForIdleSync();
但仍然没有运气。
Is this the only way to do this? Or am I missing something?
这是唯一的方法吗?或者我错过了什么?
Thanks in advance.
提前致谢。
回答by Oleksandr Kucherenko
On my mind correct approach will be:
在我看来,正确的方法是:
/** 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();
}
};
}
And then pattern of usage will be:
然后使用模式将是:
// wait during 15 seconds for a view
onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15)));
回答by Hesam
Thanks to AlexK for his awesome answer. There are cases that you need to make some delay in code. It is not necessarily waiting for server response but might be waiting for animation to get done. I personally have problem with Espresso idolingResources (I think we are writing many lines of code for a simple thing) so I changed the way AlexK was doing into following code:
感谢 AlexK 的精彩回答。在某些情况下,您需要在代码中进行一些延迟。它不一定在等待服务器响应,但可能在等待动画完成。我个人对 Espresso idleingResources 有问题(我认为我们正在为一个简单的事情编写多行代码),所以我将 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);
}
};
}
So you can create a Delay
class and put this method in it in order to access it easily.
You can use it in your Test class same way: onView(isRoot()).perform(waitFor(5000));
因此,您可以创建一个Delay
类并将此方法放入其中以便轻松访问它。你可以在你的 Test 类中以同样的方式使用它:onView(isRoot()).perform(waitFor(5000));
回答by MattMatt
I stumbled upon this thread when looking for an answer to a similar problem where I was waiting for a server response and changing visibility of elements based on response.
在寻找类似问题的答案时,我偶然发现了这个线程,我正在等待服务器响应并根据响应更改元素的可见性。
Whilst the solution above definitely helped, I eventually found this excellent example from chiukiand now use that approach as my go-to whenever I'm waiting for actions to occur during app idle periods.
虽然上面的解决方案确实有帮助,但我最终从 chiuki找到了这个很好的例子,现在每当我在应用程序空闲期间等待操作发生时,我都会使用这种方法作为我的首选。
I've added ElapsedTimeIdlingResource()to my own utilities class, can now effectively use that as an Espresso-proper alternative, and now usage is nice and clean:
我已将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);
回答by Cabezas
I think it's more easy to add this line:
我认为添加这一行更容易:
SystemClock.sleep(1500);
Waits a given number of milliseconds (of uptimeMillis) before returning. Similar to sleep(long), but does not throw InterruptedException; interrupt() events are deferred until the next interruptible operation. Does not return until at least the specified number of milliseconds has elapsed.
在返回之前等待给定的毫秒数(uptimeMillis)。类似于 sleep(long),但不抛出 InterruptedException;中断()事件被推迟到下一个可中断的操作。至少在指定的毫秒数过后才返回。
回答by Roc Boronat
You can just use Barista methods:
您可以只使用咖啡师方法:
BaristaSleepActions.sleep(2000);
BaristaSleepActions.sleep(2000);
BaristaSleepActions.sleep(2, SECONDS);
BaristaSleepActions.sleep(2, SECONDS);
Barista is a library that wraps Espresso to avoid adding all the code needed by the accepted answer. And here's a link! https://github.com/SchibstedSpain/Barista
Barista 是一个包装 Espresso 的库,以避免添加已接受答案所需的所有代码。这是一个链接! https://github.com/SchibstedSpain/Barista
回答by anna3101
I'm new to coding and Espresso, so while I know the good and reasonable solution is to use idling, I'm not yet intelligent enough to do that.
我是编码和 Espresso 的新手,所以虽然我知道使用空闲是好的和合理的解决方案,但我还不够聪明,无法这样做。
Until I become more knowledgeable, I still need my tests to somehow run though, so for now I'm using this dirty solution which makes a number of attempts at finding an element, stops if it finds it and if not, briefly sleeps and starts again until it reach the max nr of attempts (the highest number of attempts so far has been around 150).
在我变得更有知识之前,我仍然需要以某种方式运行我的测试,所以现在我正在使用这个肮脏的解决方案,它尝试了多次寻找元素,如果找到就停止,如果没有,则短暂睡眠并开始直到达到最大尝试次数(迄今为止的最高尝试次数约为 150 次)。
private static boolean waitForElementUntilDisplayed(ViewInteraction element) {
int i = 0;
while (i++ < ATTEMPTS) {
try {
element.check(matches(isDisplayed()));
return true;
} catch (Exception e) {
e.printStackTrace();
try {
Thread.sleep(WAITING_TIME);
} catch (Exception e1) {
e.printStackTrace();
}
}
}
return false;
}
I'm using this in all the methods that are finding elements by ID, text, parent etc:
我在通过 ID、文本、父级等查找元素的所有方法中都使用了它:
static ViewInteraction findById(int itemId) {
ViewInteraction element = onView(withId(itemId));
waitForElementUntilDisplayed(element);
return element;
}
回答by Big McLargeHuge
This is similar to this answerbut uses a timeout instead of attempts and can be chained with other ViewInteractions:
这与此答案类似,但使用超时而不是尝试,并且可以与其他 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()
}
Usage:
用法:
onView(withId(R.id.whatever))
.waitUntilVisible(5000)
.perform(click())
回答by Bolhoso
Espresso is built to avoid sleep() calls in the tests. Your test should not open a dialog to enter an IP, that should be the tested activity's responsability.
Espresso 旨在避免测试中的 sleep() 调用。您的测试不应该打开一个对话框来输入 IP,这应该是被测试活动的责任。
On the other hand, your UI test should:
另一方面,您的 UI 测试应该:
- Wait for the IP dialog to appear
- Fill in the IP address and click enter
- Wait for your button to appear and click it
- 等待 IP 对话框出现
- 填写IP地址,点击回车
- 等待您的按钮出现并单击它
The test should look something like this:
测试应如下所示:
// type the IP and press OK
onView (withId (R.id.dialog_ip_edit_text))
.check (matches(isDisplayed()))
.perform (typeText("IP-TO-BE-TYPED"));
onView (withText (R.string.dialog_ok_button_title))
.check (matches(isDisplayed()))
.perform (click());
// now, wait for the button and click it
onView (withId (R.id.button))
.check (matches(isDisplayed()))
.perform (click());
Espresso waits for everything that is happening both in the UI thread and the AsyncTask pool to finish before executing your tests.
在执行测试之前,Espresso 会等待 UI 线程和 AsyncTask 池中发生的所有事情完成。
Remember that your tests shouldn't do anything that is your application responsability. It should behave like an "well informed user": a user that clicks, verify that something is shown in the screen, but, as a matter of fact, know the IDs of the components
请记住,您的测试不应该做任何由您的应用程序负责的事情。它应该表现得像一个“消息灵通的用户”:点击的用户,验证屏幕上显示的东西,但事实上,知道组件的 ID
回答by Gastón Saillén
You should use Espresso Idling Resource, it's suggested at this CodeLab
您应该使用 Espresso Idling Resource,在此CodeLab 中建议使用
An idling resource represents an asynchronous operation whose results affect subsequent operations in a UI test. By registering idling resources with Espresso, you can validate these asynchronous operations more reliably when testing your app.
空闲资源表示异步操作,其结果会影响 UI 测试中的后续操作。通过向 Espresso 注册空闲资源,您可以在测试应用程序时更可靠地验证这些异步操作。
Example of an Asynchronous call from the Presenter
来自 Presenter 的异步调用示例
@Override
public void loadNotes(boolean forceUpdate) {
mNotesView.setProgressIndicator(true);
if (forceUpdate) {
mNotesRepository.refreshData();
}
// The network request might be handled in a different thread so make sure Espresso knows
// that the app is busy until the response is handled.
EspressoIdlingResource.increment(); // App is busy until further notice
mNotesRepository.getNotes(new NotesRepository.LoadNotesCallback() {
@Override
public void onNotesLoaded(List<Note> notes) {
EspressoIdlingResource.decrement(); // Set app as idle.
mNotesView.setProgressIndicator(false);
mNotesView.showNotes(notes);
}
});
}
Dependencies
依赖关系
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'
For androidx
对于 androidx
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support.test.espresso:espresso-idling-resource:3.0.2'
Official Repo:https://github.com/googlecodelabs/android-testing
官方仓库:https : //github.com/googlecodelabs/android-testing
IdlingResource Example:https://github.com/googlesamples/android-testing/tree/master/ui/espresso/IdlingResourceSample
空闲资源示例:https : //github.com/googlesamples/android-testing/tree/master/ui/espresso/IdlingResourceSample
回答by Sean Blahovici
I will add my way of doing this to the mix:
我会将我的方法添加到组合中:
fun suspendUntilSuccess(actionToSucceed: () -> Unit, iteration : Int = 0) {
try {
actionToSucceed.invoke()
} catch (e: Throwable) {
Thread.sleep(200)
val incrementedIteration : Int = iteration + 1
if (incrementedIteration == 25) {
fail("Failed after waiting for action to succeed for 5 seconds.")
}
suspendUntilSuccess(actionToSucceed, incrementedIteration)
}
}
Called like this:
像这样调用:
suspendUntilSuccess({
checkThat.viewIsVisible(R.id.textView)
})
You can add parameters like max iterations, iteration length, etc to the suspendUntilSuccess function.
您可以向 suspendUntilSuccess 函数添加最大迭代次数、迭代长度等参数。
I still prefer using idling resources, but when the tests are acting up due to slow animations on the device for instance, I use this function and it works well. It can of course hang for up to 5 seconds as it is before failing, so it could increase the execution time of your tests if the action to succeed never succeeds.
我仍然更喜欢使用空闲资源,但是当由于设备上的动画缓慢而导致测试运行时,我使用此功能并且它运行良好。它当然可以像失败前一样挂起长达 5 秒,因此如果成功的操作永远不会成功,它可能会增加测试的执行时间。