Espresso - 设置 SeekBar

18

有人知道,在Espresso UI测试中,如何将SeekBar设置为特定值或仅通过单击该视图来实现?

我只是收到一个异常:在ID为...的视图上执行“单击”时出错。

onView(withId(R.id.FilterPriceMax)).perform(click());
5个回答

33

您可能想尝试以下代码:

首先,您可以使用其类名将 SeekBar 进行匹配:

onView(withClassName(Matchers.equalTo(SeekBar.class.getName()))).perform(setProgress(progress));

setProgress(final int progress)是你定义的一个ViewAction:

public static ViewAction setProgress(final int progress) {
        return new ViewAction() {
            @Override
            public void perform(UiController uiController, View view) {
                SeekBar seekBar = (SeekBar) view;
                seekBar.setProgress(progress);
            }
            @Override
            public String getDescription() {
                return "Set a progress on a SeekBar";
            }
            @Override
            public Matcher<View> getConstraints() {
                return ViewMatchers.isAssignableFrom(SeekBar.class);
            }
        };
    }

4
这是一个很棒的回答。可惜这些回答受到了负评。 - whizzle
setProgress() 方法中的 'int progress' 参数应该声明为 final,以便在内部类中使用。 - Theramax
运行得非常顺利。 - Hossam Hassan
2
其中一个问题是它没有设置标志 fromUser,因此如果您使用该标志,它将无法工作。 - jluiz20
如果您有两个SeekBar会怎样呢?我得到了一个错误,提示SeekBar具有多个视图(2)。 - A.Turner
这个确实可以工作,但是没有设置 fromUser 标志 :( - Quintin Balsdon

21

我有一个自定义的SeekBar,并且在我的View中有多个SeekBar。我用下面的代码成功地进行了测试:

onView(withId(R.id.my_seek_bar)).perform(setProgress(10));

public static ViewAction setProgress(final int progress) {
        return new ViewAction() {
            @Override
            public void perform(UiController uiController, View view) {
                ((MyCustomSeekBar) view).setSelectedProgress(progress);
                //or ((SeekBar) view).setProgress(progress);
            }

            @Override
            public String getDescription() {
                return "Set a progress";
            }

            @Override
            public Matcher<View> getConstraints() {
                return ViewMatchers.isAssignableFrom(RangeSeekBar.class);
            }
        };
    }

1
提到的RangeSeekbar.class是什么?你能回忆一下吗? - Monster Brain

14

我使用滑动操作在SeekBar上执行实际的滑动。这可以确保回调方法(SeekBar.OnSeekBarChangeListener.onProgressChanged)被调用时fromUser设置为true。这也更符合点击测试的要求。

public static ViewAction scrubSeekBarAction(int progress) {
    return actionWithAssertions(new GeneralSwipeAction(
            Swipe.SLOW,
            new SeekBarThumbCoordinatesProvider(0),
            new SeekBarThumbCoordinatesProvider(progress),
            Press.PINPOINT));
}

private static class SeekBarThumbCoordinatesProvider implements CoordinatesProvider {
    int mProgress;

    public SeekBarThumbCoordinatesProvider(int progress) {
        mProgress = progress;
    }

    private static float[] getVisibleLeftTop(View view) {
        final int[] xy = new int[2];
        view.getLocationOnScreen(xy);
        return new float[]{ (float) xy[0], (float) xy[1] };
    }

    @Override
    public float[] calculateCoordinates(View view) {
        if (!(view instanceof SeekBar)) {
            throw new PerformException.Builder()
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new RuntimeException(String.format("SeekBar expected"))).build();
        }
        SeekBar seekBar = (SeekBar) view;
        int width = seekBar.getWidth() - seekBar.getPaddingLeft() - seekBar.getPaddingRight();
        double progress = mProgress == 0 ? seekBar.getProgress() : mProgress;
        int xPosition = (int) (seekBar.getPaddingLeft() + width * progress / seekBar.getMax());
        float[] xy = getVisibleLeftTop(seekBar);
        return new float[]{ xy[0] + xPosition, xy[1] + 10 };
    }
}

1
这是一个有前途的解决方案,但在我的设备上返回了错误的值。 - msc87
不确定您所说的“错误值”是什么意思,但如果您的意思是,在scrubSeekBarAction之后的进度值与您设置的不同,那么在“scrub…”之前(以及之后)使用Thread.sleep(1000)似乎有所帮助。我猜这类似于“perform(click())执行长按”的问题。 - allofmex
我在LG设备API 21-23(虚拟和物理)上遇到了不准确的问题。滑动操作是正确执行的(使用“开发人员选项”中的“指针位置”进行跟踪),但是无论速度如何,“SeekBar”都无法注册最后约20%的滑动。我不知道为什么。然后,我将“GeneralSwipeAction”更改为“GeneralClickAction”,对于我的情况效果很好。这是一个点击而不是滑动。我还将“progress”添加了“0.25”,以便安全起见,因为旧的API会将单击/滑动向下舍入,而新的API会将其舍入到最近的值,尽管到目前为止并不需要这样做。 - Jure Sencar

10

如果您想要检查进度值

  public static Matcher<View> withProgress(final int expectedProgress) {
    return new BoundedMatcher<View, SeekBar>(SeekBar.class) {
      @Override
      public void describeTo(Description description) {
        description.appendText("expected: ");
        description.appendText(""+expectedProgress);
      }

      @Override
      public boolean matchesSafely(SeekBar seekBar) {
        return seekBar.getProgress() == expectedProgress;
      }
    };
  }

5

如果您不需要特定的值,我已经使用它来对seekbar UI组件进行基本测试。它假设'check()'是基于0-100百分比的。

onView(withId(R.id.yourSeekBar)).perform(new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_LEFT, Press.FINGER));
onView(withId(R.id.yourSeekBarUserFeedback)).check(matches(withText("0")));

onView(withId(R.id.yourSeekBar)).perform(new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER));
onView(withId(R.id.yourSeekBarUserFeedback)).check(matches(withText("50")));

onView(withId(R.id.yourSeekBar)).perform(new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER));
onView(withId(R.id.yourSeekBarUserFeedback)).check(matches(withText("100")));

2
GeneralClickAction() 接受 3 个参数的声明已被弃用。现在我们建议使用接受 5 个参数的 GeneralClickAction(),如此处所示:https://dev59.com/gmEh5IYBdhLWcg3wpE3w - Mr-IDE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接