【问题标题】:How to set time to MaterialDateTimePicker with Espresso如何使用 Espresso 将时间设置为 MaterialDateTimePicker
【发布时间】:2016-01-07 15:24:41
【问题描述】:

我正在尝试使用 Espresso 创建简单的 UI 测试来为新创建的项目设置日期。

项目正在使用https://github.com/wdullaer/MaterialDateTimePicker,但它显示的对话框片段具有复杂的 UI,没有什么可坚持的。

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

有什么建议吗?

【问题讨论】:

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


    【解决方案1】:

    最后我创建了 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)));
    

    【讨论】:

    • 在同一个例子中,我有一个疑问。如果假设 DayPickerView 实现 DatePickerController 而不是创建字段,那么我们无法获取字段 DayPickerView.class.getDeclaredField("mController");所以我们需要改变这种行为。
    • 然后在检查 instanceof 后进行转换
    【解决方案2】:

    我使用了内置的 TimePickerDialog。

    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 分钟。设置完成后,点击确定按钮。

    【讨论】:

      【解决方案3】:

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

      uiatomator,谷歌制作的另一个很棒的工具可以让你测试你的Android系统功能,比如通知和屏幕锁定。您可以将它与Espresso 测试框架一起使用。

      请阅读:Espresso & UIAutomator - the perfect tandem

      还有这个官方的uiautomator 文档,你可以找到here

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

      也请查看这篇文章:Crushing Fragmentation using the Factory Design Pattern with UiAutomator 请注意,在本文中作者对DatePicker 执行uiatomator 测试。

      希望对你有帮助

      【讨论】:

      • 问题是,MaterielDateTimePicker 不能分配给 androids DateTimePicker。 Id 没有一个单一的视图,而是 DialogFragment 中的复杂视图层次结构。如果是这样,我会使用 espresso 中的 PickerActions。我试图使测试结构尽可能简单,因为我不会成为编写新测试的人。但是这个时间对话框在我们的应用程序中是很常见的功能。
      • 我还没用过DatePicker和这个MaterialDateTimePicker,但我认为在那种情况下uiautomator会更有用
      【解决方案4】:

      我为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);
              }
          };
      

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

      【讨论】:

      • 看起来与我的解决方案非常相似。谢谢。
      【解决方案5】:

      我在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());
      

      【讨论】:

        【解决方案6】:

        这是Lubos' answer 的 Kotlin 翻译,虽然仅适用于 setDate

        import android.view.View
        import androidx.test.espresso.UiController
        import androidx.test.espresso.ViewAction
        import androidx.test.espresso.matcher.ViewMatchers
        import com.wdullaer.materialdatetimepicker.date.DatePickerController
        import com.wdullaer.materialdatetimepicker.date.DayPickerView
        import org.hamcrest.Matcher
        import org.hamcrest.core.AllOf
        import java.lang.reflect.Field
        
        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)));
        
        

        【讨论】:

        • 你能提供 CustomViewActions 的导入吗?
        • @Amin 我已经添加了导入。
        【解决方案7】:

        我在 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"))));
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-03-02
          • 1970-01-01
          • 2013-07-02
          • 2021-07-27
          • 2017-03-22
          • 2021-11-07
          相关资源
          最近更新 更多