I have an problem with checking if toast is displayed using espresso. I'm using class: import android.os.IBinder; import android.support.test.espresso.Root; import android.view.WindowManager; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; public class ToastMatcher extends TypeSafeMatcher<Root> { @Override public void describeTo(Description description) { description.appendText("is toast"); } @Override public boolean matchesSafely(Root root) { int type = root.getWindowLayoutParams().get().type; if ((type == WindowManager.LayoutParams.TYPE_TOAST)) { IBinder windowToken = root.getDecorView().getWindowToken(); IBinder appToken = root.getDecorView().getApplicationWindowToken(); if (windowToken == appToken) { // windowToken == appToken means this window isn't contained by any other windows. // if it was a window for an activity, it would have TYPE_BASE_APPLICATION. return true; } } return false; }}and checking Toast by:onView(withText(R.string.unauthorized)).inRoot(new ToastMatcher()) .check(matches(isDisplayed()));Everything works fine until I try to check another toast in the same class for example:@Testpublic void messageOnBack() throws Exception{pressBack();onView(withText(R.string.exit_on_back)).inRoot(new ToastMatcher()) .check(matches(isDisplayed()));Then first one is passed but second one puts error: android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with string from resource id: <2131165323>[unauthorized] value: Wrong login or password.View Hierarchy:+>LinearLayout{id=-1, visibility=VISIBLE, width=660, height=116, has-focus=false, has-focusable=false, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}|+->AppCompatTextView{id=16908299, res-name=message, visibility=VISIBLE, width=528, height=58, has-focus=false, has-focusable=false, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=66.0, y=29.0, text=Please click BACK again to exit., input-type=0, ime-target=false, has-links=false}|at dalvik.system.VMStack.getThreadStackTrace(Native Method)What is weird, when I comment out one of the tests second one works just fine without any changes. Espresso seems to get stupid when one toast is displayed on top of another. Any ideas how to solve this? 解决方案 The NoMatchingViewException you are seeing means your ToastMatcher does find a root view with TYPE_TOAST, but cannot find the requested view inside that root (otherwise, you'd get a NoMatchingRootException).I guess the reason is that Android is not showing toasts on top of each other, but one after the other. Thus, probably the only view it finds in a toast-root is your first toast (your second toast has not yet been shown). Thus, before checking for the second toast you will have to somehow wait until your first toast has disappeared. This isn't trivial, unfortunately (see below), and I believe you cannot get around changing production code.A possible solution is given in https://stackoverflow.com/a/32023568/1059766. The basic idea is to attach a OnAttachStateChangedListener to your toast's view before showing the toast, and use that listener to track when the view is attached to and detached from the view hierarchy. This can then be used to implement a custom IdlingResource that can wait for a toast to disappear.The way espresso waits for things is by means of IdlingResources. I currently cannot see how you could create a custom idling resource to wait for a toast without changing production code. Therefore, something along the lines of the aformentioned answer is the best I can think of, even though the required change to production code is not very appealing.That being said, note that your ToastMatcher solution (that's often suggested on stackoverflow and blogs) is also not a really reliable way to test toasts. It works in most situations, but not always. Consider e.g. the following snippet:new AsyncTask<Void, Void, Void>() { public void doInBackground(...) { // start background work for 10s (or just Thread.sleep(10000)) }}.execute()Toast.make(context, R.string.mytoast, Toast.LENGTH_SHORT).show()As espresso always waits until the UI thread and all async-tasks are idle, it will in the above example wait for (about) 10s until the isDisplayed() check is performed. But at that point the toast will have disappeared and therefore the check fails. I hope this is enough to illustrate the inherent problem with this approach. The following statement from Valera Zakharov in https://groups.google.com/d/msg/android-test-kit-discuss/uaHdXuVm-Bw/cuQASd3PdpgJ seems to confirm that there is no easy solution to test toasts with espresso: Zakharov then also suggests to add some hooks to the production code (as far as I understand that). Thus, I guess adding an IdlingResource based on some production code hooks is really the best you can do (this might also make your toast testing more stable in general, as you can then test your toasts as outlined by Zakharov). 这篇关于浓咖啡检查是否显示烤面包(一个烤面包在另一个烤面包上)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-26 23:17