浓咖啡检查是否显示烤面包(一个烤面包在另一个烤面包上) [英] Espresso checking if toasts are displayed (one on top of another)

查看:86
本文介绍了浓咖啡检查是否显示烤面包(一个烤面包在另一个烤面包上)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在检查是否使用浓缩咖啡显示吐司时遇到问题.我正在使用课程:

       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;
    }

}

并通过以下方式检查Toast:

onView(withText(R.string.unauthorized)).inRoot(new ToastMatcher())
            .check(matches(isDisplayed()));

一切正常,直到我尝试检查同一个班级的其他吐司,例如:

@Test
public void messageOnBack() throws Exception{
pressBack();
onView(withText(R.string.exit_on_back)).inRoot(new ToastMatcher())
            .check(matches(isDisplayed()));

然后第一个通过,但是第二个输入错误:

    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)

奇怪的是,当我注释掉其中一项测试时,第二项工作正常,没有任何更改.当一个烤面包在另一个烤面包上显示时,Espresso似乎变得愚蠢.有什么想法可以解决这个问题吗?

解决方案

您看到的NoMatchingViewException表示您的ToastMatcher确实找到了具有TYPE_TOAST的根视图,但是无法在该根内部找到请求的视图(否则,您会得到一个NoMatchingRootException).

我猜想原因是Android并不是在[em]上互相展示[em] ,而是在一个[em>另一个] 之后显示敬酒.因此,它可能在吐司根中找到的唯一视图是您的第一个吐司(您的第二个吐司尚未显示).因此,在检查第二个吐司之前,您必须以某种方式等待直到您的第一个吐司消失为止.不幸的是,这并非微不足道(请参阅下文),我相信您无法绕过更改生产代码.

https://stackoverflow.com/a/32023568/1059766 中给出了可能的解决方案.基本思想是在显示烤面包的 之前,在烤面包的视图上附加一个OnAttachStateChangedListener,并使用该侦听器跟踪该视图何时附加到视图层次结构或从视图层次结构分离.然后可以使用它来实现自定义的IdlingResource,它可以等待烤面包消失.

意式浓缩咖啡等待事物的方式是通过IdlingResource s.我目前看不到如何在不更改生产代码的情况下如何创建自定义的空闲资源来等待吐司.因此,尽管可以对生产代码进行必要的更改,但我能想到的是上述答案中最好的方法.

话虽如此,请注意,您的ToastMatcher解决方案(通常在stackoverflow和博客上建议)也不是测试吐司的真正可靠方法.它在大多数情况下都有效,但并非总是如此.考虑例如以下代码段:

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()

由于espresso总是等待直到UI线程和所有异步任务都处于空闲状态,因此在上面的示例中,它将等待(大约)10秒钟,直到执行isDisplayed()检查为止.但是到那时,吐司将消失,因此检查失败.我希望这足以说明这种方法的固有问题.以下Valera Zakharov在 https://中发表的声明groups.google.com/d/msg/android-test-kit-discuss/uaHdXuVm-Bw/cuQASd3PdpgJ 似乎证实没有简单的方法可以用意式浓缩咖啡测试吐司:

简短的回答:不幸的是,Android中没有线程安全的方法来执行此操作,因此我们不在Espresso中提供此功能.
细节: 实施Toast的方式可以检测到已显示的Toast.但是,无法通过调用show()来查看是否已请求Toast,也无法在show()与Toast可见之间的时间段之间进行阻塞.这带来了无法解决的计时问题(您只能通过睡眠和希望来解决)[...].

Zakharov然后还建议在生产代码中添加一些挂钩(据我所知).因此,我想基于某些生产代码挂钩添加IdlingResource确实是您可以做的最好的事情(这也可能使您的吐司测试总体上更加稳定,因为您可以按照Zakharov的说明测试吐司).

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:

@Test
public 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:

Short answer: Unfortunately, there is no thread-safe way of doing this in Android, so we don't provide this capability in Espresso.
Details: The way Toasts are implemented makes it possible to detect a toast has been displayed. However there is no way to see if a Toast has been requested, thru a call to show()) or to block between the period of time between show() and when the toast has become visible. This is opens up unresolvable timing issues (that you can only address thru sleep & hope) [...].

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).

这篇关于浓咖啡检查是否显示烤面包(一个烤面包在另一个烤面包上)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆