如何使用 Espresso 将时间设置为 MaterialDateTimePicker
How to set time to MaterialDateTimePicker with Espresso
我正在尝试创建简单的 UI 测试,使用 Espresso 为新创建的项目设置日期。
项目正在使用 https://github.com/wdullaer/MaterialDateTimePicker,但它显示的对话框片段很复杂 UI,没有什么可坚持的。
我想创建自定义 ViewAction 以设置类似于 Espresso 中的 PickerActions 的日期或时间。
有什么建议吗?
我很确定用 Espresso 测试它是不可能的。您可能需要执行此操作另一个名为 uiatomator
.
的 UI 测试工具
uiatomator
,由 Google 制作的另一个很棒的工具允许您测试 Android 系统功能,如通知和屏幕锁定。您可以将它与 Espresso
测试框架一起使用。
请阅读:Espresso & UIAutomator - the perfect tandem
和这份官方 uiautomator
文档,您可以找到 here。
此外,我已经发现 uiautomator
有一个特殊的 class 用于测试这个 UI 元素,称为 DatePickerHelper.java
。
另请查看这篇文章:Crushing Fragmentation using the Factory Design Pattern with UiAutomator
请注意,在本文中,作者对 DatePicker
.
执行了 uiatomator
测试
希望对您有所帮助
我为 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);
}
};
很确定还有改进的余地,但它确实有效。试一试。
最后我创建了也可以设置时间的 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)));
我在 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());
我使用了内置的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 分钟。设置完成后,点击确定按钮。
这是 的 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)));
我在 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"))));
}
我正在尝试创建简单的 UI 测试,使用 Espresso 为新创建的项目设置日期。
项目正在使用 https://github.com/wdullaer/MaterialDateTimePicker,但它显示的对话框片段很复杂 UI,没有什么可坚持的。
我想创建自定义 ViewAction 以设置类似于 Espresso 中的 PickerActions 的日期或时间。
有什么建议吗?
我很确定用 Espresso 测试它是不可能的。您可能需要执行此操作另一个名为 uiatomator
.
uiatomator
,由 Google 制作的另一个很棒的工具允许您测试 Android 系统功能,如通知和屏幕锁定。您可以将它与 Espresso
测试框架一起使用。
请阅读:Espresso & UIAutomator - the perfect tandem
和这份官方 uiautomator
文档,您可以找到 here。
此外,我已经发现 uiautomator
有一个特殊的 class 用于测试这个 UI 元素,称为 DatePickerHelper.java
。
另请查看这篇文章:Crushing Fragmentation using the Factory Design Pattern with UiAutomator
请注意,在本文中,作者对 DatePicker
.
uiatomator
测试
希望对您有所帮助
我为 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);
}
};
很确定还有改进的余地,但它确实有效。试一试。
最后我创建了也可以设置时间的 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)));
我在 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());
我使用了内置的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 分钟。设置完成后,点击确定按钮。
这是 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)));
我在 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"))));
}