由于RunTimeException,RecyclerView Espresso测试失败 [英] RecyclerView Espresso Testing Fails Due to RunTimeException

查看:46
本文介绍了由于RunTimeException,RecyclerView Espresso测试失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用以下代码,尝试设置Espresso:

I have the following code I am using, trying to set up Espresso:

import android.support.test.espresso.Espresso;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.action.ViewActions.click;

@RunWith(AndroidJUnit4.class)
public class EspressoTest {




    @Rule
    public ActivityTestRule<MainActivity> firstRule = new ActivityTestRule<>(MainActivity.class);




    @Test
    public void testRecyclerViewClick() {
        Espresso.onView(ViewMatchers.withId(R.id.recycler_view_ingredients)).perform(RecyclerViewActions.actionOnItemAtPosition(0, click()));




    }

}






    }

它不会成功运行,我不明白为什么.错误如下:

It will not run successfully and I do not understand why. Below is the error:

 Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(is assignable from class: class android.support.v7.widget.RecyclerView and is displayed on the screen to the user)
Target view: "RecyclerView{id=2131165335, res-name=recycler_view_ingredients, visibility=VISIBLE, width=1440, height=0, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=true, is-focusable=true, is-layout-requested=false, is-selected=false, layout-params=android.support.constraint.ConstraintLayout$LayoutParams@caad301, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=0}"
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:245)
at android.support.test.espresso.ViewInteraction.access$100(ViewInteraction.java:63)
at android.support.test.espresso.ViewInteraction$1.call(ViewInteraction.java:153)
at android.support.test.espresso.ViewInteraction$1.call(ViewInteraction.java:150)

完整的Github回购: https://github.com/troy21688/KitchenPal

Full Github Repo: https://github.com/troy21688/KitchenPal

该测试实际上通过了仿真器,但没有通过我的实际手机(Google Nexus 6).这使我相信,这与每个设备上的屏幕尺寸呈现方式有关.

The test actually passed on an emulator, but not my actual phone (Google Nexus 6). It leads me to believe it has something to do with how the screen size is being rendered on each device.

推荐答案

ID为recycler_view_ingredientsRecyclerView的高度为wrap_content,因此当它没有子代或适配器为空时,该高度将为为0.该错误表示将不执行操作,因为未显示目标视图RecyclerView(height=0),这也意味着当时尚未加载数据.

Your RecyclerView with id recycler_view_ingredients has a height of wrap_content, so when it has no children or the adapter is empty, then the height will be 0. The error says that action will not be performed because the target view RecyclerView is not displayed (height=0), which also means that the data has not loaded yet at the time.

您的应用程序正在异步地在其他线程上加载数据,然后在完全加载后在主线程上更新您的RecyclerView.作为一件事情 实际上,Espresso仅在主线程上进行同步,因此,当您的应用开始在后台加载数据时,它认为该应用的主线程处于空闲状态,因此它将继续执行操作,它是否会失败取决于设备性能.

Your app is loading data asynchronously on different thread, then update your RecyclerView on the main thread when it has completely loaded. As a matter of fact, Espresso only synchronizes on main thread, so when your app starts to load data in the background, it thinks that the main thread of the app has gone idle, and so it proceeds to perform the action, which may or may not fail depends on devices performance.

解决此问题的一种简单方法是增加一些延迟,例如一秒钟:

An easy way to fix this issue is to add some delay, say a second:

Thread.sleep(1000);
onView(withId(R.id.recycler_view_ingredients)).perform(actionOnItemAtPosition(0, click()));

或者,修复它的一种优雅方法是使用IdlingResource:

Or, an elegant way to fix it is to use IdlingResource:

onView(withId(R.id.recycler_view_ingredients))
    .perform(
        waitUntil(hasItemCount(greaterThan(0))), // wait until data has loaded
        actionOnItemAtPosition(0, click()));

这是一些免费的课程:

public static Matcher<View> hasItemCount(Matcher<Integer> matcher) {
    return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {

        @Override public void describeTo(Description description) {
            description.appendText("has item count: ");
            matcher.describeTo(description);
        }

        @Override protected boolean matchesSafely(RecyclerView view) {
            return matcher.matches(view.getAdapter().getItemCount());
        }
    };
}

public static ViewAction waitUntil(Matcher<View> matcher) {
    return actionWithAssertions(new ViewAction() {
        @Override public Matcher<View> getConstraints() {
            return ViewMatchers.isAssignableFrom(View.class);
        }

        @Override public String getDescription() {
            StringDescription description = new StringDescription();
            matcher.describeTo(description);
            return String.format("wait until: %s", description);
        }

        @Override public void perform(UiController uiController, View view) {
            if (!matcher.matches(view)) {
                LayoutChangeCallback callback = new LayoutChangeCallback(matcher);
                try {
                    IdlingRegistry.getInstance().register(callback);
                    view.addOnLayoutChangeListener(callback);
                    uiController.loopMainThreadUntilIdle();
                } finally {
                    view.removeOnLayoutChangeListener(callback);
                    IdlingRegistry.getInstance().unregister(callback);
                }
            }
        }
    });
}

private static class LayoutChangeCallback implements IdlingResource, View.OnLayoutChangeListener {

    private Matcher<View> matcher;
    private IdlingResource.ResourceCallback callback;
    private boolean matched = false;

    LayoutChangeCallback(Matcher<View> matcher) {
        this.matcher = matcher;
    }

    @Override public String getName() {
        return "Layout change callback";
    }

    @Override public boolean isIdleNow() {
        return matched;
    }

    @Override public void registerIdleTransitionCallback(ResourceCallback callback) {
        this.callback = callback;
    }

    @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        matched = matcher.matches(v);
        callback.onTransitionToIdle();
    }
}

这篇关于由于RunTimeException,RecyclerView Espresso测试失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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