使用Espresso测试Android PreferenceFragment

时间:2017-07-18 16:33:27

标签: android testing android-espresso

我正在尝试在“设置”中为PreferenceFragments片段编写测试。

但是,我一直收到这个错误: android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: is assignable from class: class android.widget.AdapterView

测试代码如下:

@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsFragmentTest {

    @Rule
    public ActivityTestRule<SettingsActivity> mActivityRule = new ActivityTestRule<>(
            SettingsActivity.class);

    @Test
    public void preferredLocationShouldBeVisibleOnDisplay(){
        mActivityRule.getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                SettingsFragment settingsFragment = startSettingsFragment();
            }
        });

        // This check passes correctly
        onView(withId(R.id.weather_settings_fragment))
                .check(matches(isCompletelyDisplayed()));

        // This check gives me the NoMatchingViewException
        onData(allOf(is(instanceOf(Preference.class)),
                withKey("location")))
                .check(matches(isCompletelyDisplayed()));
    }

    private SettingsFragment startSettingsFragment(){
        SettingsActivity activity = mActivityRule.getActivity();
        FragmentTransaction transaction = activity.getSupportFragmentManager().beginTransaction();
        SettingsFragment settingsFragment = new SettingsFragment();

        transaction.replace(R.id.weather_settings_fragment, settingsFragment, "settingsFragment");
        transaction.commit();

        return settingsFragment;
    }
}

settings_activity布局如下所示:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:name="com.example.android.sunshine.SettingsFragment"
          android:id="@+id/weather_settings_fragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

首选项屏幕布局如下:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">

    <EditTextPreference
        android:defaultValue="@string/pref_location_default"
        android:inputType="text"
        android:key="@string/pref_location_key"
        android:singleLine="true"
        android:title="@string/pref_location_label" />

    <ListPreference
        android:defaultValue="@string/pref_units_metric"
        android:entries="@array/pref_units_options"
        android:entryValues="@array/pref_units_values"
        android:key="@string/pref_units_key"
    android:title="@string/pref_units_label" />

<CheckBoxPreference
    android:defaultValue="@bool/show_notifications_by_default"
    android:key="@string/pref_enable_notifications_key"
    android:summaryOff="@string/pref_enable_notifications_false"
    android:summaryOn="@string/pref_enable_notifications_true"
    android:title="@string/pref_enable_notifications_label" />

</PreferenceScreen>

我无法在线找到有关如何测试PreferenceFragments的任何示例或信息。大多数信息与测试活动有关。

6 个答案:

答案 0 :(得分:4)

PreferenceMatchers似乎只能与Android框架中的首选项类一起使用,而不能与支持首选项库(com.android.support:preference-v14)一起使用。由于后者内部使用了RecylerView,因此我可以通过使用espresso-contrib中的RecyclerViewActions来掌握首选项:

onView(withId(R.id.list))
       .perform(RecyclerViewActions.actionOnItem(hasDescendant(withText(R.string.pref_manage_categories_title)),
            click()));

答案 1 :(得分:1)

您可以将onData()allOf()withTitle()结合使用:

onData(allOf(is(
        instanceOf(Preference.class)),
        withTitle(R.string.my_pref_string)))
        .perform(click());

或将onData()allOf()withKey()一起使用:

onData(allOf(is(instanceOf(Preference.class)), withKey("myPrefKey")))
        .onChildView(withText(R.string.my_pref_string))
        .perform(click());

或将onData()anything()一起使用,但这可能不可靠并导致超时:

// atPosition() is required - Not sure why
onData(anything())
        .atPosition(3)
        .onChildView(withText(R.string.my_pref_string))
        .perform(click());

以类似的方式声明或检查首选项:

onData(allOf(is(
        instanceOf(Preference.class)),
        withTitle(R.string.my_pref_string)))
        .check(matches(isDisplayed()));

注意:要快速添加这些方法的导入,请将闪烁的光标放在未解决的方法上,然后执行Android Studio➔帮助➔查找操作➔搜索"show intention action"➔单击结果选项➔弹出窗口窗口将出现➔单击"Import static method ..."。您也可以将键盘快捷方式分配给“显示意图动作”。 More info here。另一种方法是在“设置”中启用"Add unambiguous imports on the fly"

答案 2 :(得分:1)

导航到设置活动后,您可以将Espresso withText()与首选项android:title一起使用

@Rule
public ActivityTestRule<SettingsActivity> mActivityRule = new ActivityTestRule<>(
        SettingsActivity.class);

onView(withText(mActivityRule.getActivity().getResources().getString(R.string.my_pref_title))
    .perform(click());

旁注::如果它是ListPreference,则按如上所述在其上单击时,可以测试从列表中选择项目的方式相同,但可以使用列表withText()中的输入文本,此处没有标题。

onView(withText(mActivityRule.getActivity().getResources().getString(R.string.list_entry_sring))
    .perform(click());

答案 3 :(得分:0)

我通常使用Espresso通过活动测试片段。只需测试Fragment公开的UI,就好像它是在ActivityTestRule开始的Activity中一样。如果在活动的初始启动时不存在Fragment,则在测试中导航的方式与用户启动Fragment事务的方式相同。如果你发现自己需要测试需要你在某种测试工具中站起来的业务逻辑,这通常是一个很好的指标,它应该被拉到一个单独的类中,可以(理想情况下)单元测试,没有任何Android的依赖关系。

答案 4 :(得分:0)

您可以随时尝试Record Espresso测试 https://developer.android.com/studio/test/espresso-test-recorder.html

以下是有关如何使用列表的提示 https://developer.android.com/training/testing/espresso/lists.html

这是公开API的PreferenceMatcher

    onData(PreferenceMatchers.withKey(mContext.getString(R.string.key_settings)))
         .perform(click());

答案 5 :(得分:0)

@Test
    public void clickListPreference() throws Exception{

        // Check if it is displayed
        Context appContext = InstrumentationRegistry.getTargetContext();

        onData(allOf(
           is(instanceOf(Preference.class)),
           withKey(appContext.getResources().getString(R.string.pref_units_key))))
          .check(matches(isDisplayed()));

        // Check if click is working
        onData(allOf(
           is(instanceOf(Preference.class)),
           withKey(appContext.getResources().getString(R.string.pref_units_key))))           
          .onChildView(withText(appContext.getResources()
              .getString(R.string.pref_units_label))).perform(click());
 }

希望这会对您有所帮助。