如何使用Espresso设置MaterialDateTimePicker的时间

时间:2016-01-07 15:24:41

标签: android android-testing ui-testing android-espresso

我尝试使用Espresso创建简单的UI测试,以便为新创建的项目设置日期。

项目正在使用https://github.com/wdullaer/MaterialDateTimePicker,但它显示了具有复杂UI的对话框片段,无法保留。

我想创建自定义ViewAction来设置类似于Espresso的PickerActions的日期或时间。

有任何建议吗?

7 个答案:

答案 0 :(得分:3)

我使用了内置的TimePickerDialog。

Looks like this

fun setAlarmTime(){

    val calendar = Calendar.getInstance()

    onView(isAssignableFrom(TimePicker::class.java)).perform(
        PickerActions.setTime(
            calendar.get(Calendar.HOUR_OF_DAY),
            calendar.get(Calendar.MINUTE) + 2
        )
    )
    onView(withText("OK")).perform(click())
}

首先,您应该打开TimePickerDialog,然后使用此代码。时间将是当前时间+ 2分钟。设置完成后,单击确定按钮。

答案 1 :(得分:2)

我很确定用Espresso测试它是不可能的。您可能需要将此操作作为另一个名为uiatomator的UI测试工具。

uiatomator,Google提供的另一款出色工具可让您测试Android系统功能,例如通知和屏幕锁定。您可以将它与Espresso测试框架一起使用。

请阅读:Espresso & UIAutomator - the perfect tandem

以及此官方uiautomator文档,您会找到here

另外,我已经发现uiautomator有一个特殊的类来测试这个名为DatePickerHelper.java的UI元素。

同时查看本文:Crushing Fragmentation using the Factory Design Pattern with UiAutomator            请注意,在本文中,作者对uiatomator执行了DatePicker次测试。

希望有所帮助

答案 2 :(得分:2)

最后我创建了能够设置时间的ViewAction,但是它必须知道对话框中的视图类名,因为它必须与Matcher匹配。

  /**
     * Returns a {@link ViewAction} that sets a date on a {@link DatePicker}.
     */
    public static ViewAction setDate(final int year, final int monthOfYear, final int dayOfMonth) {

        return new ViewAction() {

            @Override
            public void perform(UiController uiController, View view) {
                final DayPickerView dayPickerView = (DayPickerView) view;

                try {
                    Field f = null; //NoSuchFieldException
                    f = DayPickerView.class.getDeclaredField("mController");
                    f.setAccessible(true);
                    DatePickerController controller = (DatePickerController) f.get(dayPickerView); //IllegalAccessException
                    controller.onDayOfMonthSelected(year, monthOfYear, dayOfMonth);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public String getDescription() {
                return "set date";
            }

            @SuppressWarnings("unchecked")
            @Override
            public Matcher<View> getConstraints() {
                return allOf(isAssignableFrom(DayPickerView.class), isDisplayed());
            }
        };

    }

    /**
     * Returns a {@link ViewAction} that sets a time on a {@link TimePicker}.
     */
    public static ViewAction setTime(final int hours, final int minutes) {

        return new ViewAction() {

            @Override
            public void perform(UiController uiController, View view) {
                final RadialPickerLayout timePicker = (RadialPickerLayout) view;

                timePicker.setTime(new Timepoint(hours, minutes, 0));
            }

            @Override
            public String getDescription() {
                return "set time";
            }

            @SuppressWarnings("unchecked")
            @Override
            public Matcher<View> getConstraints() {
                return allOf(isAssignableFrom(RadialPickerLayout.class), isDisplayed());
            }
        };

    }

用法:

onView(isAssignableFrom(DayPickerView.class)).perform(MaterialPickerActions.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)));

onView(isAssignableFrom(RadialPickerLayout.class)).perform(MaterialPickerActions.setTime(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE)));

答案 3 :(得分:1)

我为similar library编写了此视图操作。

public static ViewAction setCalendarDatePicker(final DateTime dateTime){
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            Matcher standardConstraint = ViewMatchers.isDisplayingAtLeast(90);
            return standardConstraint;
        }

        @Override
        public String getDescription() {
            return "setCalendarDatePicker";
        }

        @Override
        public void perform(UiController uiController, View view) {
            MonthAdapter.CalendarDay day = new MonthAdapter.CalendarDay(dateTime.getMillis());
            DayPickerView datePicker = (DayPickerView) view;
            SimpleMonthAdapter adapter = (SimpleMonthAdapter) datePicker.getAdapter();
            datePicker.goTo(day, false, true, true);
            adapter.onDayClick(null, day);
        }
    };

非常确定有改进的余地,但它确实有效。试一试。

答案 4 :(得分:1)

我在https://github.com/wdullaer/MaterialDateTimePicker投入了一些时间,并根据如何处理日,月和年提出了以下解决方案。

点击日期:

我无法使用robotium或Expresso处理日期。您需要在gradle文件中添加uiautomator依赖项。 在gradle文件中添加以下行

androidTestCompile 'com.android.support:support-annotations:23.4.0'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'

然后在项目gradle文件中将min sdk升级到18,以便UiAutomator工作。

现在您可以运行以下代码来设置日期

     if (Build.VERSION.SDK_INT >= 23) {
                UiDevice device = UiDevice.getInstance(getInstrumentation());

//25 is the index which will click on 26 or 24 you need to figure out which one will work for you
                UiObject allowPermissions = device.findObject(new UiSelector().index(25));
                if (allowPermissions.exists()) {
                    try {
                        allowPermissions.click();
                    } catch (UiObjectNotFoundException e) {
                        Log.e("ERROR",e.toString());
                    }
                }
            }

点击年份:

onView(withId(R.id.date_picker_year)).perform(click());
//If you want to set it as 2018. 
onView(withText("2018")).perform(click());

点击月:

  //Check how many swipeUp or swipeDown's are 
    onView(withId(R.id.animator)).perform(swipeUp());

答案 5 :(得分:0)

这是Lubos' answer的科特林翻译,尽管仅适用于setDate

class CustomViewActions {
    companion object {
        fun setDate(year: Int, monthOfYear: Int, dayOfMonth: Int): ViewAction {
            return object: ViewAction {
                override fun getConstraints(): Matcher<View> {
                    return allOf(isAssignableFrom(DayPickerView::class.java), isDisplayed())
                }

                override fun getDescription(): String {
                    return "set date"
                }

                override fun perform(uiController: UiController?, view: View?) {
                    try {
                        var f: Field? = null;
                        f = DayPickerView::class.java.getDeclaredField("mController")
                        f.isAccessible = true
                        val controller: DatePickerController = f.get(view) as DatePickerController
                        controller.onDayOfMonthSelected(year, monthOfYear, dayOfMonth)
                    } catch (e: NoSuchFieldException) {
                        e.printStackTrace()
                    } catch (e: IllegalAccessException) {
                        e.printStackTrace()
                    }
                }

            }


        }
    }
}
onView(isAssignableFrom(DayPickerView::class.java)).perform(CustomViewActions.setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)));

答案 6 :(得分:0)

我使用 Android Studio 中的 LayoutInspector 检查了视图的 ID。如果它们在以后的版本中不会改变,那么这些是 ID 以及它对我的作用:

public static void setDate(LocalDate date){
    onView(withTagValue((Matchers.is((Object) ("TOGGLE_BUTTON_TAG"))))).perform(click());
    onView(withId(com.google.android.material.R.id.mtrl_picker_text_input_date)).perform(replaceText(date.format(DateTimeFormatter.ofPattern("M/d/yy"))));
}

public static void setTime(LocalTime time){
    onView(withId(com.google.android.material.R.id.material_timepicker_mode_button)).perform(click());
    onView(withId(com.google.android.material.R.id.material_hour_text_input)).perform(click());
    onView(allOf(isDisplayed(), withClassName(is(AppCompatEditText.class.getName())))).perform(replaceText(time.format(DateTimeFormatter.ofPattern("hh"))));
    onView(withId(com.google.android.material.R.id.material_minute_text_input)).perform(click());
    onView(allOf(isDisplayed(), withClassName(is(AppCompatEditText.class.getName())))).perform(replaceText(time.format(DateTimeFormatter.ofPattern("mm"))));
}