使用Espresso测试Android PreferenceFragment

Mar*_*loe 4 testing android 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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" />
Run Code Online (Sandbox Code Playgroud)

“首选项”屏幕的布局如下:

<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>
Run Code Online (Sandbox Code Playgroud)

我还没有找到关于如何测试PreferenceFragments的任何示例或信息。大多数信息与测试活动有关。

mto*_*nig 6

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()));
Run Code Online (Sandbox Code Playgroud)

  • 我确认PreferenceMatchers都不适用于Android X库。RecyclerViewAction确实可以工作,我只需要用R.id.recycler_view替换R.id.list(在这种情况下,UI Automator Viewer可能会有所帮助)。 (4认同)
  • 更准确地说:“androidx.preference.R.id.recycler_view” (2认同)

Mr-*_*IDE 5

对于 PreferenceFragment:

PreferenceFragment使用一个ListView内部,所以你可以使用onData()allOf()withTitle()

onData(allOf(is(
        instanceOf(Preference.class)),
        withTitle(R.string.my_pref_string)))
        .perform(click());
Run Code Online (Sandbox Code Playgroud)

onData()allOf()和一起使用withKey()

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

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

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

以类似的方式断言或检查首选项:

onData(allOf(is(
        instanceOf(Preference.class)),
        withTitle(R.string.my_pref_string)))
        .check(matches(isDisplayed()));
Run Code Online (Sandbox Code Playgroud)

对于 PreferenceFragmentCompat:

PreferenceFragmentCompat使用RecyclerView内部,所以你必须使用意式咖啡的contrib库,如答案被提到mtotschnig 3 Septemebr 2018如果您使用的是androidx.preference:preference:1.x.x写在科特林单元测试库,它可能会非常棘手知道该怎么用. 你可以从这样的事情开始:

onView(withId(androidx.preference.R.id.recycler_view))
    .perform(actionOnItem<RecyclerView.ViewHolder>(
        hasDescendant(withText(R.string.my_pref_title)), click()))
Run Code Online (Sandbox Code Playgroud)

注意:要快速添加这些方法的导入,请将闪烁的光标放在未解析的方法上,然后执行 Android Studio ? Help? Find Action? 搜索"show context action""show intention action"? 单击结果选项?会出现一个弹出窗口?点击"Import static method ..."。您还可以为“显示上下文操作”分配键盘快捷键。更多信息在这里。另一种方法是"Add unambiguous imports on the fly"在设置中启用。


jdo*_*yer 0

我通常通过 Espresso 的活动来测试 Fragment。只需测试 Fragment 公开的 UI,就像它在您使用 ActivityTestRule 启动的 Activity 中一样。如果在首次启动活动时片段不存在,请以与用户启动片段事务相同的方式在测试中导航。如果您发现自己需要测试需要您在某种测试工具中建立 Fragments 的业务逻辑,那么这通常是一个很好的指标,表明应该将其拉出到一个单独的类中,该类可以(理想地)在没有任何 Android 的情况下进行单元测试依赖关系。