Можно ли остановить холостой ход для завершения AsyncTask? - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь настроить тест для своего проекта - проверить, отображается ли индикатор выполнения, когда мое приложение выполняет запрос к серверу.

Тестируемый код использует AsyncTask для выполнения сетевого вызова.

Я создал блокирующий сервер (MockWebServer) для перехвата и удержания сетевого вызова - он получает запрос, но не предоставляет ответ, пока я не позвоню ". release()". Это позволяет мне проверить, прежде чем ответ сервера происходит.

Моя логика выглядит так:

// Mock server will catch the next network request
BlockingServer blockingServer = createBlockingServer();

// onResume() activity performs network request and shows Progress Spinner
activityTestRule.launchActivity(null);

// onView() waits on UiController.loopUntilIdle() <- Fails here due to timeout.
onView(withId(progressBar)).check(matches(isDisplayed()));

// Tells the server to respond to the network request
blockingServer.release();

onView(withId(progressBar)).check(matches(not(isDisplayed()))); 

Моя проблема в том, что поскольку тестируемый код использует AsyncTask для запроса к серверу, Espresso естественным образом блокирует вызов проверки (onView()), чтобы дождаться завершения AsyncTask перед проверкой.

Что мне нужно, так это временно остановить работу Espresso на холостом ходу во время ожидания AsyncTask, чтобы выполнить проверку, когда сервер блокирует поток логики приложения.

(Изменение тестируемого кода недоступно)

Может кто-нибудь помочь?

1 Ответ

0 голосов
/ 16 января 2019

Итак ... это ответ, к которому я пришел, и кое-что стоящее за ним:

Espresso (в частности, звонки на onView(), onData(), injectEvent и Actions) использует UiControllerImpl.loopMainThreadUntilIdle(), чтобы дождаться, пока все "вызывающие холостые" сигналы будут ложными. Он проходит по AsyncTask, CompatAsyncTask и тому, что называется dynamicIdle, чтобы все бездействовали.

Когда этот метод возвращает основной поток продолжается.

loopMainThreadUtilIdle() проверяет IdleNotifier, чтобы проверить состояние простоя каждого из этих трех элементов. Очевидно, что если вы хотите остановить эспрессо в ожидании AsyncTask, asyncIdle представляет для вас особый интерес.

Классы IdleNotifier передаются в UiControllerImpl при его построении - это происходит через кинжал, поэтому вам нужно взглянуть на DaggerBaseLayerComponent, который использует провайдеры, чтобы получить аргументы конструкции и передать их в UiControllerProvider для построения это.

Все во всех этих классах заперты очень плотно. Видимость метода и класса обычно защищена или является частной и финальной.

Единственным способом, который я нашел, было создание моего собственного класса Espresso.java (onView () и onData ()), в котором использовался пользовательский DaggerBaseLayerComponent, позволяющий мне использовать либо: моих собственных провайдеров, либо мой собственный UiController.

Я обнаружил, что это не решает всей проблемы. Существует еще один механизм, который необходимо закодировать - когда вы начинаете деятельность, в классе Instrumentation используется waitForIdleSync. Обычно это тот Runner, который указан в вашем файле Gradle. Я создал свой собственный AndroidJUnitRunner и предоставил его в Gradle, чтобы я мог вернуться с waitForIdleSync по команде.

И, наконец, в startActivitySync в базовом классе Instrumentation он использует массив объектов ActivityWaiter для удержания ваших вызовов launchIntent(). Я не мог придумать разумного способа избежать этого, поэтому я обманул и создал этот метод в своем Runner:

    public void clearActivityWaitQueue() {
        Object mSync = Whitebox.getInternalState(this, "mSync");
        List mWaitingActivities = Whitebox.getInternalState(this, "mWaitingActivities");
        if (mSync != null && mWaitingActivities != null) {
            mWaitingActivities.clear();
            synchronized (mSync) {
                mSync.notifyAll();
            }
        }
    }

Он использует PowerMock, чтобы дать мне удобные методы Whitebox для установки внутреннего состояния Инструментовки:

// Used to give access to Whitebox
androidTestImplementation 'org.powermock:powermock-reflect:1.6.5'

И это все! Легко ли?

( Пожалуйста, скажите мне, что это проще, чем это и как !! )

...