0

私はSettingsActivityクラスをテストしようとしていますが、私はAmbiguousViewMatcherExceptionを得ています。ここでエスプレッソ:複数のリストビュー

は私のテストケースです:

android.support.test.espresso.AmbiguousViewMatcherException: 'is assignable from class: class android.widget.AdapterView' matches multiple views in the hierarchy. 
Problem views are marked with '****MATCHES****' below. 

View Hierarchy: 
... 
+------->LinearLayout{id=16909142, res-name=headers, visibility=VISIBLE, width=600, height=887, has-focus=false, has-focusable=false, has-window-focus=true, 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=2} 
| 
+-------->ListView{id=16908298, res-name=list, visibility=VISIBLE, width=536, height=823, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, 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=32.0, y=32.0, child-count=0} ****MATCHES****** 
| 
+-------->FrameLayout{id=16909143, res-name=list_footer, visibility=VISIBLE, width=536, height=0, has-focus=false, has-focusable=false, has-window-focus=true, 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=32.0, y=855.0, child-count=0} 
| 
+------>RelativeLayout{id=16909146, res-name=button_bar, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2} 
| 
+------->AppCompatButton{id=16909147, res-name=back_button, visibility=VISIBLE, width=0, height=0, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Tilbage, input-type=0, ime-target=false, has-links=false} 
| 
+------->LinearLayout{id=-1, visibility=VISIBLE, width=0, height=0, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2} 
| 
+-------->AppCompatButton{id=16909148, res-name=skip_button, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Spring over, input-type=0, ime-target=false, has-links=false} 
| 
+-------->AppCompatButton{id=16909149, res-name=next_button, visibility=VISIBLE, width=0, height=0, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, text=Næste, input-type=0, ime-target=false, has-links=false} 
| 
+----->LinearLayout{id=-1, visibility=VISIBLE, width=600, height=887, has-focus=true, has-focusable=true, has-window-focus=true, 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=3} 
| 
+------>ListView{id=16908298, res-name=list, visibility=VISIBLE, width=600, height=887, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=true, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=12} ****MATCHES****** 
| 
+------->AppCompatTextView{id=16908310, res-name=title, visibility=VISIBLE, width=584, height=35, has-focus=false, has-focusable=false, has-window-focus=true, 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=8.0, y=0.0, text=Synkronisering, input-type=0, ime-target=false, has-links=false} 
... 

onData()試合2つのListViewの何らかの理由で、私はちょうどこれを防ぐ方法を見つけ出すことはできません、:

@Test 
public void whenHospitalSettingEmpty_shouldDisplaySummary() throws Exception { 
    Resources res = getInstrumentation().getTargetContext().getResources(); 

    mPreferenceEditor.putString(
      res.getString(R.string.activity_settings_hospital_key), 
      ""); 

    mActivityTestRule.launchActivity(null); 

    String expected = res.getString(R.string.activity_settings_hospital_summary); 

    onData(allOf(
      is(instanceOf(Preference.class)), 
      withKey(res.getString(R.string.activity_settings_hospital_key)), 
      withSummary(R.string.activity_settings_hospital_summary), 
      withTitle(R.string.activity_settings_hospital_title))) 
      .onChildView(withText(expected)) 
      .check(matches(isDisplayed())); 

} 

ここではログ出力があります最初のListViewがどこから来たのか分かりません。ここで

は私のpreference.xmlです:

<?xml version="1.0" encoding="utf-8"?> 
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> 
    <PreferenceCategory 
     android:title="@string/activity_settings_sync_category_title" 
     android:key="@string/activity_settings_sync_category_key"> 

     <SwitchPreference 
      android:key="@string/activity_settings_sync_key" 
      android:summary="@string/activity_settings_sync_summary" 
      android:title="@string/activity_settings_sync_title" 
      android:defaultValue="true"/> 

    </PreferenceCategory> 

    <PreferenceCategory 
     android:title="@string/activity_settings_site_category_title" 
     android:key="@string/activity_settings_site_category_key" 
     android:summary="@string/activity_settings_site_category_summary"> 


     <EditTextPreference 
      android:key="@string/activity_settings_hospital_key" 
      android:title="@string/activity_settings_hospital_title" 
      android:summary="@string/activity_settings_hospital_summary" 
      android:dialogTitle="@string/activity_settings_hospital_dialog_title" 
      android:dialogMessage="@string/activity_settings_hospital_dialog_message" 
      android:inputType="textCapWords" /> 

     <EditTextPreference 
      android:key="@string/activity_settings_department_key" 
      android:title="@string/activity_settings_department_title" 
      android:summary="@string/activity_settings_department_summary" 
      android:dialogTitle="@string/activity_settings_department_dialog_title" 
      android:dialogMessage="@string/activity_settings_department_dialog_message" 
      android:inputType="textCapWords" /> 

    </PreferenceCategory> 

    <PreferenceCategory 
     android:title="@string/activity_settings_notification_ringtone_category_title" 
     android:key="@string/activity_settings_notification_ringtone_category_key"> 


     <RingtonePreference 
      android:key="@string/activity_settings_notification_ringtone_key" 
      android:title="@string/activity_settings_notification_ringtone_title" 
      android:summary="@string/activity_settings_notification_ringtone_summary" 
      android:dialogTitle="@string/activity_settings_notification_ringtone_dialog_title" 
      android:dialogMessage="@string/activity_settings_notification_ringtone_dialog_message"/> 

    </PreferenceCategory> 

    <PreferenceCategory 
     android:title="@string/activity_settings_dicom_category_title" 
     android:key="@string/activity_settings_dicom_category_key"> 

     <SwitchPreference 
       android:key="@string/activity_settings_dicom_worklist_key" 
       android:title="@string/activity_settings_dicom_worklist_title" 
       android:summary="@string/activity_settings_dicom_worklist_summary" /> 

      <!--<EditTextPreference--> 
       <!--android:key="dcmwl_ae_title_preference"--> 
       <!--android:title="@string/pref_dicom_settings_worklist_ae_title"--> 
       <!--android:dialogMessage="@string/pref_dicom_settings_worklist_ip_dialog_text"--> 
       <!--android:dialogTitle="@string/pref_dicom_settings_worklist_ip_dialog_title"--> 
       <!--android:dependency="dcmwl_enable"/>--> 
      <!--<EditTextPreference--> 
       <!--android:defaultValue="10.0.0.2"--> 
       <!--android:key="dcmwl_ip_preference"--> 
       <!--android:title="@string/pref_dicom_settings_worklist_ip"--> 
       <!--android:dependency="dcmwl_enable" />--> 
      <!--<EditTextPreference--> 
       <!--android:key="dcmwl_port_preference"--> 
       <!--android:title="@string/pref_dicom_settings_worklist_port"--> 
       <!--android:dependency="dcmwl_enable"/>--> 

     <SwitchPreference 
      android:key="@string/activity_settings_dicom_pacs_key" 
      android:title="@string/activity_settings_dicom_pacs_title" 
      android:summary="@string/activity_settings_dicom_pacs_summary"/> 

      <!--<EditTextPreference--> 
       <!--android:key="pacs_ae_title_preference"--> 
       <!--android:title="@string/pref_dicom_settings_pacs_ae_title"--> 
       <!--android:dependency="pacs_enable"/>--> 
      <!--<EditTextPreference--> 
       <!--android:defaultValue="10.0.0.2"--> 
       <!--android:key="pacs_ip_preference"--> 
       <!--android:title="@string/pref_dicom_settings_pacs_ip"--> 
       <!--android:dependency="pacs_enable"/>--> 
      <!--<EditTextPreference--> 
       <!--android:key="pacs_port_preference"--> 
       <!--android:title="@string/pref_dicom_settings_pacs_port"--> 
       <!--android:dependency="pacs_enable"/>--> 

    </PreferenceCategory> 

    <PreferenceCategory 
     android:title="@string/activity_settings_debug_category_title" 
     android:key="@string/activity_settings_debug_category_key"> 

     <SwitchPreference 
      android:key="@string/activity_settings_debug_key" 
      android:summary="@string/activity_settings_debug_summary" 
      android:title="@string/activity_settings_debug_title" 
      android:defaultValue="false"/> 

    </PreferenceCategory> 

</PreferenceScreen> 

更新: は、このような最初の答えから提案されたソリューションを変更しようとしました:

private static Matcher<View> withResName(final String resName) { 

     return new TypeSafeMatcher<View>() { 
      @Override 
      public void describeTo(Description description) { 
       description.appendText("with res-name: " + resName); 
       Timber.d("Resourcename: ", resName); 
      } 

      @Override 
      public boolean matchesSafely(View view) { 
       Timber.d("View ID: ", view.getId()); 
       String matchableResName = view.getResources().getResourceEntryName(view.getId()); 
       return !TextUtils.isEmpty(matchableResName) && matchableResName.equals(resName); 
      } 
     }; 
    } 

テストは次のようになります。

@Test 
    public void whenHospitalSettingEmpty_shouldDisplaySummary() throws Exception { 
     Resources res = getInstrumentation().getTargetContext().getResources(); 

     mPreferenceEditor.putString(
       res.getString(R.string.activity_settings_hospital_key), 
       "").commit(); 

     mActivityTestRule.launchActivity(null); 

     String expected = res.getString(R.string.activity_settings_hospital_summary); 

//  onView(withText(expected)).check(matches(isDisplayed())); 

     onData(allOf(
       is(instanceOf(Preference.class)), 
       withKey(res.getString(R.string.activity_settings_hospital_key)), 
       withSummary(R.string.activity_settings_hospital_summary), 
       withTitle(R.string.activity_settings_hospital_title))) 
       .onChildView(withText(expected)) 
       .inAdapterView(withParent(not(withResName("headers")))) 
       .check(matches(isDisplayed())); 


    } 

しかし、それはまだ動作しません。ログ出力は次のようになります。私はPreferenceCategoryが内部ListViewを作成したとし

... 
I/TestRunner: android.content.res.Resources$NotFoundException: 
Unable to find resource ID #0xffffffff 
    at android.content.res.Resources.getResourceEntryName(Resources.java:2127) 
    at com.conhea.smartgfr.preferences.SettingsActivityTest$2.matchesSafely(SettingsActivityTest.java:153) 
    at com.conhea.smartgfr.preferences.SettingsActivityTest$2.matchesSafely(SettingsActivityTest.java:143) 
    at org.hamcrest.TypeSafeMatcher.matches(TypeSafeMatcher.java:65) 
    at org.hamcrest.core.IsNot.matches(IsNot.java:25) 
    at android.support.test.espresso.matcher.ViewMatchers$26.matchesSafely(ViewMatchers.java:892) 
    at android.support.test.espresso.matcher.ViewMatchers$26.matchesSafely(ViewMatchers.java:883) 
    at org.hamcrest.TypeSafeMatcher.matches(TypeSafeMatcher.java:65) 
... 

答えて

0

。したがって、あなたの階層に複数のAdapterViewがあると、AmbiguousViewMatcherExceptionが発生します。

限り、あなたはないは、これらのListView秒のidを持ってないと、あなたはViewMatcher.withId()とのマッチングを実行することはできません。したがって、ListViewの間の重要なものに基づいてカスタムマッチャーを作成する必要があります。

残念ながら、それらのListViewには同じ属性があるため、親レベルで一致を実行する必要があります。それらの親はLinearLayoutです。気づいているように、実際に興味のないものの1つは属性res-name=headersです。

これはあなたにリソース名がない「ヘッダ」をある親を持つListViewのマッチングを実行するためのヒントを与えるだろう。

エスプレッソ言語でそれをやろうとしましょう。

 


     onData(allOf(
        Matchers.is(instanceOf(Preference.class), 
        ... // other matchers here))) 
      .onChildView(withText(expected)) 
      .inAdapterView(withParent(not(new ResourceNameMatcher("headers")))) 
      .check(matches(isDisplayed())); 

 

注:ない特定のリソースの名前を持つそれでは、Viewに一致する実行でき

 


    class ResourceNameMatcher extends TypeSafeMatcher { 

     private final String resName; 

     public ResourceNameMatcher(String resName) { 
     this.resName = resName; 
     } 

     @Override protected boolean matchesSafely(View item) { 
     Resources resources = item.getResources(); 
     String matchableResName = resources.getResourceName(item.getId()); 
     return !TextUtils.isEmpty(matchableResName) && matchableResName.equals(resName); 
     } 

     @Override public void describeTo(Description description) { 
     description.appendText("with res-name: " + resName); 
     } 
    } 

 

、:初は、のは、特定のリソース名でViewにマッチするオブジェクトを、書いてみましょうこれは単なるスケッチコードであり、私がそれをテストしていない限り動作しないかもしれません。それにもかかわらず、そのコンセプトはうまくいくはずです。

+0

これは動作しません。 item.getId()はIDを返しません。なんらかの理由で、android.R.id.headersのidが隠されています。レイアウトインスペクタでは、線形レイアウトにIDというヘッダがあり、デバッグ出力に整数値があることがわかるので、これは本当に奇妙です。 – Bohsen

+0

'item.getId()の代わりに' resources.getIdentifier( "headers"、 "id"、 "android")を実行します。 – azizbekian

+0

getIdentifier()はソリューションの一部でした。助けてくれてありがとう。 – Bohsen

0

私のSettingsActivityをテストするための私のソリューションです。

テスト:

@Test 
public void whenHospitalSettingEmpty_shouldDisplaySummary() throws Exception { 
    Resources res = getInstrumentation().getTargetContext().getResources(); 

    // Set the hospital preferencevalue to empty 
    mPreferenceEditor.putString(
      res.getString(R.string.activity_settings_hospital_key), 
      "").commit(); 

    // Launch activity 
    mActivityTestRule.launchActivity(null); 

    // When hospital value is empty we want to display the summary from our string ressources 
    // instead of our preferencevalue 
    onPreferenceRow(allOf(
       withKey(res.getString(R.string.activity_settings_hospital_key)), 
       withSummary(R.string.activity_settings_hospital_summary))) 
      .check(matches(isDisplayed())); 
} 

マイヘルパーメソッド:

... 

private static Matcher<View> withResName(final String resName) { 

    return new TypeSafeMatcher<View>() { 
     @Override 
     public void describeTo(Description description) { 
      description.appendText("with res-name: " + resName); 
     } 

     @Override 
     public boolean matchesSafely(View view) { 
      int identifier = view.getResources().getIdentifier(resName, "id", "android"); 
      return !TextUtils.isEmpty(resName) && (view.getId() == identifier); 
     } 
    }; 
} 

private static DataInteraction onPreferenceRow(Matcher<? extends Object> datamatcher) { 

    DataInteraction interaction = onData(datamatcher); 

    return interaction 
     .inAdapterView(allOf(
      withId(android.R.id.list), 
      not(withParent(withResName("headers"))))); 
} 
関連する問題