使用Espresso和RxJava测试无尽的滚动RecyclerView

Div*_*ers 16 android automated-tests ui-testing rx-java android-espresso

我有RecyclerView无尽的滚动.因此,当用户到达last - 2列表中的位置时,我呼叫服务器以获取更多数据,并且在通话过程中,我再添加一个项目 - 进度一项.

现在,我正在尝试使用Espresso编写体面的UI测试,它将检查无限滚动当前是否正在工作:

    @Test
    public void checkIfProgressShown() {
        InstaFeed feed = TestDataFactory.makeInstaFeed(20);
        InstaFeed oldFeed = TestDataFactory.makeInstaFeed(20);
               when(mockDataManager.getFeedItemsFromServer()).thenReturn(Observable.just(feed.getInstaItems()));
        when(mockDataManager.getOldFeedItemsFromServer()).thenReturn(Observable.just(oldFeed.getInstaItems())
                .delay(2, TimeUnit.SECONDS));

        instaActivityActivityTestRule.launchActivity(null);

        int position = 0;
        for (InstaItem item : feed.getInstaItems()) {
            onView(withId(R.id.recycler_view))
               .perform(RecyclerViewActions.scrollToPosition(position));
            onView(withText(item.getLocation().getName()))
                .check(matches(isDisplayed())); // Line of crash
            position++;
        }

        onView(withId(R.id.progress))
                .check(matches(isDisplayed()));
    }
Run Code Online (Sandbox Code Playgroud)

所以基本上说,我试图延迟Observable的响应,显示新的一批项目,以便Espresso可以向下滚动到最后一项并使我的progress项目可见.问题在于测试只是堆栈,从UI的角度来看它看起来像:

不要混淆,这里没有进度条 - 在设备上禁用动画,所以没关系.最后,小项是有进展的项目

60秒后它崩溃了 -

android.support.test.espresso.AppNotIdleException: Looped for 3019 iterations over 60 SECONDS. The following Idle Conditions failed .
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:580)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:82)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:53)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.check(ViewInteraction.java:158)
at org.kidinov.mvp_test.InstaActivityTest.checkIfProgressShown(InstaActivityTest.java:69)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:257)
at org.kidinov.mvp_test.test.common.TestComponentRule$1.evaluate(TestComponentRule.java:49)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:240)
at org.kidinov.mvp_test.runner.UnlockDeviceAndroidJUnitRunner.onStart(UnlockDeviceAndroidJUnitRunner.java:36)
at org.kidinov.mvp_test.runner.RxAndroidJUnitRunner.onStart(RxAndroidJUnitRunner.java:16)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1853)
Run Code Online (Sandbox Code Playgroud)

我使用RxAndroidJUnitRunner作为Runner.

可能我做错了,没有必要模拟延迟?


UPD:如果你想玩这种情况,有准备好的项目.那个班里面的问题.

ssw*_*zek 0

我花了一些时间修复了你的测试。希望它能帮助你。

\n\n

1.您以错误的方式生成测试数据。在makeInstaItem您传递i索引(以及0值)时,列表中的最后一项与 .0L创建的进度项具有相同的创建时间 ( ) InstaAdapter:

\n\n
private static InstaItem makeInstaItem(String prefix, int i, int date\xd0\xa1oefficient) {\n    InstaItem item = new InstaItem();\n    item.setLocation(new Location(prefix + "_location_" + i));\n    item.setCreatedTime(i * 1000000L * date\xd0\xa1oefficient);\n    item.setImages(makeInstaImages());\n    item.setId(randomUuid());\n    return item;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这应该更改为例如:

\n\n
private static InstaItem makeInstaItem(String prefix, int i, int date\xd0\xa1oefficient) {\n    InstaItem item = new InstaItem();\n    item.setLocation(new Location(prefix + "_location_" + i));\n    item.setCreatedTime((i + 1) * 1000000L * date\xd0\xa1oefficient);\n    item.setImages(makeInstaImages());\n    item.setId(randomUuid());\n    return item;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

创建的测试列表将作为适配器的参考传递。添加进度项后,它是最后一项之前的一项。在测试中,您尝试匹配此进度栏项目,就像它是常规位置项目一样,因此它会崩溃。在我进行所有更改之后,它被添加为最后一项,并在测试循环到达它之前被删除。

\n\n

2.\n当调用订阅InstaPresenter时,您没有添加项目:loadMore

\n\n
subscribe(x -> {\n    Timber.d("load more - items fetched");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

更改为例如:

\n\n
subscribe(items -> {\n    if (!items.isEmpty()) {\n        getMvpView().showFeed(items);\n    }\n    Timber.d("load more - items fetched");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

3.你根本不需要拖延。保持实现简单,例如:

\n\n
when(mockDataManager.getOldFeedItemsFromServer()).thenReturn(Observable.just(oldFeed.getInstaItems())\n            .observeOn(AndroidSchedulers.mainThread())\n            .doOnNext(x -> {\n                Timber.d("On next after delay");\n                when(mockDataManager.subscribeOnFeedItemsChanges())\n                        .thenReturn(Observable.just(oldFeed.getInstaItems()));\n            }));\n\ninstaActivityActivityTestRule.launchActivity(null);\n\nint position = 0;\nfor (InstaItem item : feed.getInstaItems()) {\n    onView(withId(R.id.recycler_view))\n                .perform(RecyclerViewActions.scrollToPosition(position));\n    onView(withText(item.getLocation().getName()))\n                .check(matches(isDisplayed()));\n    onView(withText(InstaAdapter.dateFormat.format(new Date(item.getCreatedTime() * 1000))))\n                .check(matches(isDisplayed()));\n    position++;\n}\n\nfor (InstaItem item : oldFeed.getInstaItems()) {\n    onView(withId(R.id.recycler_view))\n                .perform(RecyclerViewActions.scrollToPosition(position));\n    onView(withText(item.getLocation().getName()))\n                .check(matches(isDisplayed()));\n    onView(withText(InstaAdapter.dateFormat.format(new Date(item.getCreatedTime() * 1000))))\n                .check(matches(isDisplayed()));\n    position++;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  1. 只是我个人的小建议。我宁愿用单元测试来测试演示者逻辑,也不愿费力地进行 UI 测试。
  2. \n
\n\n

我录制了视频来展示上述更改后它的工作原理

\n