Как ждать задачу asyn c в Espresso без IdlingResource - PullRequest
0 голосов
/ 11 января 2020

У меня есть следующий код эспрессо:

@Test
fun backgroundWorkDisplaysTextAfterLoading() {
    onView(withId(R.id.btn_next_fragment)).perform(click())
    onView(withId(R.id.btn_background_work)).perform(click())

    onView(withId(R.id.hiddenTextView)).check(matches(isDisplayed()))
}

При щелчке по btn_background_work вызывается сервис, который через 2 секунды возвращает ответ, а затем скрытый текст становится видимым.

Как можно подождать, пока Espresso выполнит ViewAssertion (check (соответствует (isDisplayed))), пока этот вызов службы не вернет значение? Вызов службы выполняется с помощью Retrofit, а вызов службы выполняется в потоке Schedulers.io ().

Я не могу коснуться производственного кода, поэтому реализация IdlingResources в производственном коде невозможна.

Любая помощь приветствуется!

Ответы [ 2 ]

0 голосов
/ 13 января 2020

Использование ресурсов на холостом ходу не требует обновления кода на производстве.

Но это также можно сделать без использования ресурса на холостом ходу, как вы и просили, вы можете использовать этот пользовательский ViewAction:

/**
 * Perform action of waiting until the element is accessible & not shown.
 * @param viewId The id of the view to wait for.
 * @param millis The timeout of until when to wait for.
 */
public static ViewAction waitUntilShown(final int viewId, final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewId + "> is shown during " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + millis;
            final Matcher<View> viewMatcher = withId(viewId);

            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                    // found view with required ID
                    if (viewMatcher.matches(child) && child.isShown()) {
                        return;
                    }
                }

                uiController.loopMainThreadForAtLeast(50);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withActionDescription(this.getDescription())
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new TimeoutException())
                    .build();
        }
    };
}

И вы можете использовать его следующим образом:

onView(isRoot()).perform(waitUntilShown(R.id.hiddenTextView, 5000));

при необходимости обновляя время ожидания 5 секунд (5000 миллис).

0 голосов
/ 13 января 2020

Вы все еще можете реализовать IdlingResource, не касаясь производственного кода. Вам нужно только создать обратный вызов IdlingResource, который прослушивает ViewTreeObserver.OnDrawListener:

private class ViewPropertyChangeCallback(private val matcher: Matcher<View>, private val view: View) : IdlingResource, ViewTreeObserver.OnDrawListener {

    private lateinit var callback: IdlingResource.ResourceCallback
    private var matched = false

    override fun getName() = "View property change callback"

    override fun isIdleNow() = matched

    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
        this.callback = callback
    }

    override fun onDraw() {
        matched = matcher.matches(view)
        callback.onTransitionToIdle()
    }
}

Затем создайте пользовательский ViewAction для ожидания совпадения:

fun waitUntil(matcher: Matcher<View>): ViewAction = object : ViewAction {

    override fun getConstraints(): Matcher<View> {
        return any(View::class.java)
    }

    override fun getDescription(): String {
        return StringDescription().let {
            matcher.describeTo(it)
            "wait until: $it"
        }
    }

    override fun perform(uiController: UiController, view: View) {
        if (!matcher.matches(view)) {
            ViewPropertyChangeCallback(matcher, view).run {
                try {
                    IdlingRegistry.getInstance().register(this)
                    view.viewTreeObserver.addOnDrawListener(this)
                    uiController.loopMainThreadUntilIdle()
                } finally {
                    view.viewTreeObserver.removeOnDrawListener(this)
                    IdlingRegistry.getInstance().unregister(this)
                }
            }
        }
    }
}

И обновите ваш тест для использования действия:

onView(withId(R.id.hiddenTextView)).perform(waitUntil(isDisplayed()))
// or
onView(withId(R.id.hiddenTextView)).perform(waitUntil(withEffectiveVisibility(VISIBLE)))
    .check(matches(isDisplayed()))

Без IdlingResource, Thread.sleep(...) может быть вашим следующим вариантом, но он будет нестабильным или неэффективным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...