Kar*_*lin 7 android unit-testing android-espresso
我正在尝试在具有回收器视图的屏幕之一中实施仪器测试。当我使用 时,我似乎没有问题scrollTo(),但是当我开始使用actionOnItem()相同的视图匹配器时,我收到运行时异常。
任何帮助,将不胜感激。谢谢!
测试代码
@Test
public void nextButton_disabledWhilePhoneNumberInvalid () {
onView(withId(R.id.countrycodepicker_enterphonenumber_ccp)).perform(click());
// This is working
// onView(withId(R.id.recycler_countryDialog))
// .perform(scrollTo(hasDescendant(first(withText("Philippines (PH)")))));
// This is also working
// onView(withId(R.id.recycler_countryDialog))
// .perform(actionOnItemAtPosition(1, click()));
// This is not working and is causing a PerformException
onView(withId(R.id.recycler_countryDialog))
.perform(actionOnItem(hasDescendant(first(withText("Philippines (PH)"))), click()));
}
private <T> Matcher<T> first(final Matcher<T> matcher) {
return new BaseMatcher<T>() {
boolean isFirst = true;
@Override
public boolean matches(final Object item) {
if (isFirst && matcher.matches(item)) {
isFirst = false;
return true;
}
return false;
}
@Override
public void describeTo(final Description description) {
description.appendText("should return first matching item");
}
};
}
Run Code Online (Sandbox Code Playgroud)
运行时错误
android.support.test.espresso.PerformException: Error performing
'performing ViewAction: single click on item matching: holder with view: has descendant: should return first matching item' on view 'with id: com.example:id/recycler_countryDialog'.
at android.support.test.espresso.PerformException$Builder.build(PerformException.java:83)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:80)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:56)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
at com.example.EnterPhoneNumberScreenTest.nextButton_disabledWhilePhoneNumberInvalid(EnterPhoneNumberScreenTest.java:53)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)
Caused by: java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
at java.util.ArrayList.get(ArrayList.java:308)
at android.support.test.espresso.contrib.RecyclerViewActions$ActionOnItemViewAction.perform(RecyclerViewActions.java:232)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:144)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Run Code Online (Sandbox Code Playgroud)
RecyclerView适配器代码(从我正在使用的第三方库中提取)
class CountryCodeAdapter extends
RecyclerView.Adapter<CountryCodeAdapter.CountryCodeViewHolder> {
List<Country> filteredCountries = null, masterCountries = null;
TextView textView_noResult;
CountryCodePicker codePicker;
LayoutInflater inflater;
EditText editText_search;
Dialog dialog;
Context context;
CountryCodeAdapter(Context context, List<Country> countries, CountryCodePicker codePicker, final EditText editText_search, TextView textView_noResult, Dialog dialog) {
this.context = context;
this.masterCountries = countries;
this.codePicker = codePicker;
this.dialog = dialog;
this.textView_noResult = textView_noResult;
this.editText_search = editText_search;
this.inflater = LayoutInflater.from(context);
this.filteredCountries = getFilteredCountries("");
setSearchBar();
}
private void setSearchBar() {
if (codePicker.isSelectionDialogShowSearch()) {
setTextWatcher();
} else {
editText_search.setVisibility(View.GONE);
}
}
/**
* add textChangeListener, to apply new query each time editText get text changed.
*/
private void setTextWatcher() {
if (this.editText_search != null) {
this.editText_search.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
applyQuery(s.toString());
}
});
if(codePicker.isKeyboardAutoPopOnSearch()) {
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
}
}
/**
* Filter country list for given keyWord / query.
* Lists all countries that contains @param query in country's name, name code or phone code.
*
* @param query : text to match against country name, name code or phone code
*/
private void applyQuery(String query) {
textView_noResult.setVisibility(View.GONE);
query = query.toLowerCase();
//if query started from "+" ignore it
if (query.length() > 0 && query.charAt(0) == '+') {
query=query.substring(1);
}
filteredCountries= getFilteredCountries(query);
if (filteredCountries.size() == 0) {
textView_noResult.setVisibility(View.VISIBLE);
}
notifyDataSetChanged();
}
private List<Country> getFilteredCountries(String query) {
List<Country> tempCountryList = new ArrayList<Country>();
if(codePicker.preferredCountries!=null && codePicker.preferredCountries.size()>0) {
for (Country country : codePicker.preferredCountries) {
if (country.isEligibleForQuery(query)) {
tempCountryList.add(country);
}
}
if (tempCountryList.size() > 0) { //means at least one preferred country is added.
Country divider = null;
tempCountryList.add(divider);
}
}
for (Country country : masterCountries) {
if (country.isEligibleForQuery(query)) {
tempCountryList.add(country);
}
}
return tempCountryList;
}
@Override
public CountryCodeViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View rootView = inflater.inflate(R.layout.layout_recycler_country_tile, viewGroup, false);
CountryCodeViewHolder viewHolder = new CountryCodeViewHolder(rootView);
return viewHolder;
}
@Override
public void onBindViewHolder(CountryCodeViewHolder countryCodeViewHolder, final int i) {
countryCodeViewHolder.setCountry(filteredCountries.get(i));
countryCodeViewHolder.getMainView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
codePicker.setSelectedCountry(filteredCountries.get(i));
if (view != null && filteredCountries.get(i)!=null) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
dialog.dismiss();
}
}
});
}
@Override
public int getItemCount() {
return filteredCountries.size();
}
class CountryCodeViewHolder extends RecyclerView.ViewHolder {
RelativeLayout relativeLayout_main;
TextView textView_name, textView_code;
ImageView imageViewFlag;
LinearLayout linearFlagHolder;
View divider;
public CountryCodeViewHolder(View itemView) {
super(itemView);
relativeLayout_main = (RelativeLayout) itemView;
textView_name = (TextView) relativeLayout_main.findViewById(R.id.textView_countryName);
textView_code = (TextView) relativeLayout_main.findViewById(R.id.textView_code);
imageViewFlag = (ImageView) relativeLayout_main.findViewById(R.id.image_flag);
linearFlagHolder = (LinearLayout) relativeLayout_main.findViewById(R.id.linear_flag_holder);
divider = relativeLayout_main.findViewById(R.id.preferenceDivider);
}
public void setCountry(Country country) {
if(country!=null) {
divider.setVisibility(View.GONE);
textView_name.setVisibility(View.VISIBLE);
textView_code.setVisibility(View.VISIBLE);
linearFlagHolder.setVisibility(View.VISIBLE);
textView_name.setText(country.getName() + " (" + country.getNameCode().toUpperCase() + ")");
textView_code.setText("+" + country.getPhoneCode());
imageViewFlag.setImageResource(country.getFlagID());
}else{
divider.setVisibility(View.VISIBLE);
textView_name.setVisibility(View.GONE);
textView_code.setVisibility(View.GONE);
linearFlagHolder.setVisibility(View.GONE);
}
}
public RelativeLayout getMainView() {
return relativeLayout_main;
}
}
}
Run Code Online (Sandbox Code Playgroud)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:id="@+id/linear_flag_holder"
android:layout_width="40dp"
android:layout_height="50dp"
android:layout_marginLeft="@dimen/google_1x"
android:layout_marginRight="@dimen/google_1x"
android:gravity="center">
<ImageView
android:id="@+id/image_flag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/flag_india" />
</LinearLayout>
<TextView
android:id="@+id/textView_countryName"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/linear_flag_holder"
android:layout_toLeftOf="@+id/textView_code"
android:layout_toRightOf="@+id/linear_flag_holder"
android:layout_toStartOf="@+id/textView_code"
android:gravity="center_vertical"
android:text="India (IN)"
android:textColor="@android:color/secondary_text_light" />
<TextView
android:id="@+id/textView_code"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:paddingEnd="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingStart="10dp"
android:text="+91"
android:textColor="@android:color/secondary_text_light"
android:textDirection="ltr" />
<View
android:id="@+id/preferenceDivider"
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_alignParentTop="true"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="#898989"/>
</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)
更新2
如果我使用 allOf 而不是自定义的第一个 Matcher,我会因重复的子视图而收到错误,因为适配器正在重复使用前一个项目作为“分隔符”
android.support.test.espresso.PerformException: Error performing 'performing ViewAction: single click on item matching: holder with view: has descendant: (with text: is "Philippines (PH)")' on view 'with id: com.sample:id/recycler_countryDialog'.
at android.support.test.espresso.PerformException$Builder.build(PerformException.java:83)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:80)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:56)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
at com.sample.EnterPhoneNumberScreenTest.nextButton(EnterPhoneNumberScreenTest.java:59)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:270)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:262)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)
Caused by: android.support.test.espresso.PerformException: Error performing 'scroll RecyclerView to: holder with view: has descendant: (with text: is "Philippines (PH)")' on view 'RecyclerView{id=2131689704, res-name=recycler_countryDialog, visibility=VISIBLE, width=714, height=1354, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=21.0, y=269.0, child-count=12}'.
at android.support.test.espresso.PerformException$Builder.build(PerformException.java:83)
at android.support.test.espresso.contrib.RecyclerViewActions$ScrollToViewAction.perform(RecyclerViewActions.java:373)
at android.support.test.espresso.contrib.RecyclerViewActions$ActionOnItemViewAction.perform(RecyclerViewActions.java:226)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:144)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteIn
actionOnItem实际上返回一个可以调用的对象atPosition()。例如,要获取位置 1 处的第二项(即不是位置 0 处的第一项),请添加.atPosition(1)如下:
onView(withId(R.id.recycler_countryDialog))
.perform(actionOnItem<ViewHolder>(hasDescendant(withText("Philippines (PH)")), click()).atPosition(1));
Run Code Online (Sandbox Code Playgroud)
我不太确定你想用第一个()匹配器来完成什么,但我的第一个猜测是这就是导致问题的原因。
RecyclerViewActions.actionOnItem() 实际上对 ViewHolder 的 itemView 进行操作(R.layout.layout_recycler_country_tile 的根视图)。假设只有一个带有文本“Philippines (PH)”的项目,删除“第一个”匹配器应该可以工作,因为 actionOnItem 已经在查看特定的 ViewHolder 行。如果您确实有多行具有相同的文本,我建议使用 allOf() 来更好地定位您感兴趣的行。
| 归档时间: |
|
| 查看次数: |
4475 次 |
| 最近记录: |