如何使用Espresso测试RecyclerView ViewHolder文本 [英] How to Test RecyclerView ViewHolder Text with Espresso

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

问题描述

我想测试 RecyclerView 的每个 ViewHolder 中包含的文字:

  @RunWith(AndroidJUnit4.class)
公共类EspressoTest {

private Activity mMainActivity;
private RecyclerView mRecyclerView;
private int res_ID = R.id.recycler_view_ingredients;
private int itemCount = 0;

// TODO:这条规则的目的是什么,因为它与下面的测试有关?
@Rule
公共ActivityTestRule< MainActivity> firstRule = new ActivityTestRule<>(MainActivity.class);


// TODO:对Espresso测试和所需的依赖性非常困惑;它似乎Recyclerview
// TODO:需要除Android文档中提到的其他依赖项吗?
// TODO:测试RecyclerView所有视图的最佳方法是什么?在RecyclerView中填充的动态数量的视图是什么?


// TODO:来自StackOverflow的指示帖子:https://stackoverflow.com/questions/51678563/how-to-test-recyclerview-viewholder-text-with-espresso/51698252 ?noredirect = 1#comment90433415_51698252
// TODO:这有必要吗?
@Before
public void setupTest(){
this.mMainActivity = this.firstRule.getActivity();
this.mRecyclerView = this.mMainActivity.findViewById(this.res_ID);
this.itemCount = this.mRecyclerView.getAdapter()。getItemCount();

}

@Test
public void testRecyclerViewClick(){
Espresso.onView(ViewMatchers.withId(R.id.recycler_view_ingredients))。执行(RecyclerViewActions.actionOnItemAtPosition(1,ViewActions.click()));
}

//无法调用此方法,相关信息不正确
@Test
public void testRecyclerViewText(){
//检查位置处的项目3具有一些内容
onView(withRecyccyclerView(R.id.scroll_view).atPosition(3))
.check(matches(hasDescendant(withText(Some content))));


}
}

以下是我的gradle,我从来不知道RecyclerView测试需要哪些单独的依赖项:

 依赖项{
implementation fileTree(dir :'libs',包括:['* .jar'])
实现'com.android.support:recyclerview-v7:27.1.1'
implementation'com.google.android.exoplayer:exoplayer :2.6.1'
implementation'com.android.support:appcompat-v7:27.1.1'
implementation'com.android.support.constraint:constraint-layout:1.1.2'
实现'com.android.support:support-v4:27.1.1'
testImplementation'junit:junit:4.12'
androidTestImplementation'com.android.support.test:testing-support-lib:0.1 '
androidTestImplementation'com.android.support.test.espresso:espresso-core:2.0'
androidTestImplementation('com.android.support.test.espresso:espresso-contrib:2.0'){
排除组:'com.android.s upport',module:'appcompat'
排除组:'com.android.support',模块:'support-v4'
排除模块:'recyclerview-v7'
}
实现'com.android.support:support-annotations:27.1.1'
implementation'com.squareup.okhttp3:okhttp:3.10.0'
implementation'com.google.code.gson:gson :2.8.2'
implementation'com.squareup.retrofit2:converter-gson:2.4.0'
implementation'com.squareup.okhttp3:logging-interceptor:3.10.0'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation'com.android.support:cardview-v7:27.1.1'
implementation'com.google.android .exoplayer:exoplayer:2.6.0'


}

另外,如果 RecyclerView 动态填充数据怎么办?然后你根本无法硬编码你想要测试的位置....

解决方案

Espresso包 espresso-contrib 是必要的,因为它提供了 RecyclerViewActions ,它们不支持断言。

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

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
公共类TestIngredients {

/ **目标应用程序的活动* /
private IngredientsActivity mActivity;

/ ** {@link RecyclerView}的资源ID * /
private int resId = R.id.recyclerview_ingredients;

/ ** {@link RecyclerView} * /
private IngredientsLinearView mRecyclerView;

/ **和它的项目数* /
private int itemCount = 0;

/ **
*可以使用例如{@link ActivityTestRule}。对于Intent.putExtra(),
*等,可以将命令行参数传递给常规运行配置。
*此代码在{@link FragmentActivity}启动之前运行。
*也会有一个IntentsTestRule,这里不需要。
** /
@Rule
公共ActivityTestRule< IngredientsActivity> mActivityRule = new ActivityTestRule< IngredientsActivity>(IngredientsActivity.class){

@Override
protected Intent getActivityIntent(){
Intent intent = new Intent();
Bundle extras = new Bundle();
intent.putExtras(extras);
返回意图;
}
};

@Before
public void setUpTest(){

/ *从ActivityTestRule获取活动* /
this.mActivity = this.mActivityRule。 getActivity();

/ *获取活动的Ui的句柄* /
this.mRecyclerView = this.mActivity.findViewById(this.resId);
this.itemCount = this.mRecyclerView.getAdapter()。getItemCount();
}

@Test
public void RecyclerViewTest(){
if(this.itemCount> 0){
for(int i = 0; i< this.itemCount; i ++){

/ *点击项目* /
onView(withId(this.resId))
.perform(RecyclerViewActions.actionOnItemAtPosition(i) ,click()));

/ *检查是否正在显示ViewHolder * /
onView(新的RecyclerViewMatcher(this.resId)
.atPositionOnView(i,R.id.cardview))
.check(matches(isDisplayed()));

/ *检查第一项的文本* /
if(i == 0){
onView(new RecyclerViewMatcher(this.resId)
.atPositionOnView(i,R.id.ingredientName))
.check(matches(withText(Farbstoffe)));
}

}
}
}
}

而不是可以使用


I want to test the text contained in each ViewHolder of my RecyclerView:

@RunWith(AndroidJUnit4.class)
public class EspressoTest {

private Activity mMainActivity;
private RecyclerView mRecyclerView;
private int res_ID = R.id.recycler_view_ingredients;
private int itemCount = 0;

//TODO: What is the purpose of this rule as it relates to the Test below?
@Rule
public ActivityTestRule<MainActivity> firstRule = new ActivityTestRule<>(MainActivity.class);


//TODO: Very confused about Espresso testing and the dependencies required; it appears Recyclerview
//TODO: Requires additional dependencies other than those mentioned in the Android documentation?
//TODO: What would be best method of testing all views of RecyclerView? What is there is a dynamic number of Views that are populated in RecyclerView?


//TODO: Instruction from StackOverflow Post: https://stackoverflow.com/questions/51678563/how-to-test-recyclerview-viewholder-text-with-espresso/51698252?noredirect=1#comment90433415_51698252
//TODO: Is this necessary?
@Before
public void setupTest() {
    this.mMainActivity = this.firstRule.getActivity();
    this.mRecyclerView = this.mMainActivity.findViewById(this.res_ID);
    this.itemCount = this.mRecyclerView.getAdapter().getItemCount();

}

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

//CANNOT CALL THIS METHOD, THE DEPENDENCIES ARE INCORRECT
@Test
public void testRecyclerViewText() {
    // Check item at position 3 has "Some content"
    onView(withRecyclerView(R.id.scroll_view).atPosition(3))
            .check(matches(hasDescendant(withText("Some content"))));


    }
 }

Below is my gradle as well, I never understood what separate dependencies are required for RecyclerView testing:

  dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.google.android.exoplayer:exoplayer:2.6.1'
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:testing-support-lib:0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.0'
androidTestImplementation('com.android.support.test.espresso:espresso-contrib:2.0') {
    exclude group: 'com.android.support', module: 'appcompat'
    exclude group: 'com.android.support', module: 'support-v4'
    exclude module: 'recyclerview-v7'
}
implementation 'com.android.support:support-annotations:27.1.1'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:cardview-v7:27.1.1'
implementation 'com.google.android.exoplayer:exoplayer:2.6.0'


}

Also, what if the RecyclerView populates data dynamically? Then you simply could not hard code the position you wanted to test....

解决方案

Espresso package espresso-contrib is necessary, because it provides those RecyclerViewActions, which do not support assertation.

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

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
public class TestIngredients {

    /** the Activity of the Target application */
    private IngredientsActivity mActivity;

    /** the {@link RecyclerView}'s resource id */
    private int resId = R.id.recyclerview_ingredients;

    /** the {@link RecyclerView} */
    private IngredientsLinearView mRecyclerView;

    /** and it's item count */
    private int itemCount = 0;

    /**
     * such a {@link ActivityTestRule} can be used eg. for Intent.putExtra(),
     * alike one would pass command-line arguments to regular run configurations.
     * this code runs before the {@link FragmentActivity} is being started.
     * there also would be an "IntentsTestRule", just not required here.
    **/
    @Rule
    public ActivityTestRule<IngredientsActivity> mActivityRule = new ActivityTestRule<IngredientsActivity>(IngredientsActivity.class) {

        @Override
        protected Intent getActivityIntent() {
            Intent intent = new Intent();
            Bundle extras = new Bundle();
            intent.putExtras(extras);
            return intent;
        }
    };

    @Before
    public void setUpTest() {

        /* obtaining the Activity from the ActivityTestRule */
        this.mActivity = this.mActivityRule.getActivity();

        /* obtaining handles to the Ui of the Activity */
        this.mRecyclerView = this.mActivity.findViewById(this.resId);
        this.itemCount = this.mRecyclerView.getAdapter().getItemCount();
    }

    @Test
    public void RecyclerViewTest() {
        if(this.itemCount > 0) {
            for(int i=0; i < this.itemCount; i++) {

                /* clicking the item */
                onView(withId(this.resId))
                  .perform(RecyclerViewActions.actionOnItemAtPosition(i, click()));

                /* check if the ViewHolder is being displayed */
                onView(new RecyclerViewMatcher(this.resId)
                  .atPositionOnView(i, R.id.cardview))
                  .check(matches(isDisplayed()));

                /* checking for the text of the first one item */
                if(i == 0) {
                    onView(new RecyclerViewMatcher(this.resId)
                      .atPositionOnView(i, R.id.ingredientName))
                      .check(matches(withText("Farbstoffe")));
                }

            }
        }
    }
}

instead one can use a RecyclerViewMatcher for that:

public class RecyclerViewMatcher {

    private final int recyclerViewId;

    public RecyclerViewMatcher(int recyclerViewId) {
        this.recyclerViewId = recyclerViewId;
    }

    public Matcher<View> atPosition(final int position) {
        return atPositionOnView(position, -1);
    }

    public Matcher<View> atPositionOnView(final int position, final int targetViewId) {
        return new TypeSafeMatcher<View>() {
            Resources resources = null;
            View childView;
            public void describeTo(Description description) {
                String idDescription = Integer.toString(recyclerViewId);
                if(this.resources != null) {
                    try {
                        idDescription = this.resources.getResourceName(recyclerViewId);
                    } catch (Resources.NotFoundException var4) {
                        idDescription = String.format("%s (resource name not found)",
                        new Object[] {Integer.valueOf(recyclerViewId) });
                    }
                }
                description.appendText("with id: " + idDescription);
            }

            public boolean matchesSafely(View view) {
                this.resources = view.getResources();
                if (childView == null) {
                    RecyclerView recyclerView = (RecyclerView) view.getRootView().findViewById(recyclerViewId);
                    if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
                        childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
                    } else {
                        return false;
                    }
                }
                if (targetViewId == -1) {
                    return view == childView;
                } else {
                    View targetView = childView.findViewById(targetViewId);
                    return view == targetView;
                }
            }
        };
    }
}

这篇关于如何使用Espresso测试RecyclerView ViewHolder文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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