Robolectric Невозможно вызвать setValue в фоновом потоке - PullRequest
0 голосов
/ 31 октября 2019

Я тестирую AsyncTask, который onPostExecute вызывает setValue экземпляра LiveData. Поскольку я вызываю setValue из onPostExecute, никаких проблем, связанных с вызовом, выполняемым потоком пользовательского интерфейса, не ожидалось.

Тем не менее, выполнив это в модульном тесте Robolectric, я получил: java.lang.IllegalStateException: Cannot invoke setValue on a background thread

Чтобы заставить этот модульный тест ждать завершения фоновых и передних задач, я использую инструмент awaitility следующим образом:

var cf = new CompletableFuture<T>();
livedata.observe(ctrl.get(), it -> cf.complete(it));
// ... perform the AsyncTask that will update livedata in onPostExecute
await().until(() -> {
    flushBackgroundThreadScheduler()
    flushForegroundThreadScheduler()
    cf.isDone
});

Это вызов IllegalStateException: Cannot invoke setValue on a background thread на flushForegroundThreadScheduler() !! !!

Почему я получаю это исключение? И как я могу выполнить onPostExecute как в потоке пользовательского интерфейса?

ОБНОВЛЕНИЕ

Журналирование потоков кажется, что и flushBackgroundThreadScheduler(), и flushForegroundThreadScheduler()выполняется синхронно на линии. Я могу наблюдать:

LiveData created on thread 763324286
Background thread 1519527121
LiveData updated on thread 1519527121

Поскольку лямбда, переданная в await.until, работает в другом потоке, то и flushBackgroundThreadScheduler() и flushForegroundThreadScheduler() выполняются в этом потоке 1519527121.

Таким образом,Я могу решить мою проблему с помощью следующего обходного пути, запущенного в тестовом потоке, соответствующем UI-потоку. Тем не менее, мне нужно это Thread.sleep(), чтобы добиться успеха, и мне это не нравится.

Thread.sleep(1000)
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone

1 Ответ

0 голосов
/ 06 ноября 2019

Мы должны учитывать следующие соображения, касающиеся:

  1. Робоэлектрик: flushForegroundThreadScheduler() выполняется синхронно на линии.
  2. Ожидание: await.until(<Callable>) оценивает условие до в фоновом режимеthread.

Из этих двух утверждений мы видим, что вызов flushForegroundThreadScheduler() из Callable, переданного в await.until(<Callable>), приводит к вызову запланированных задач переднего плана в фоновом потоке, который отвечает на первый OPвопрос: Почему я получаю это исключение?

Отвечая на второй вопрос OP:

как мне выполнить onPostExecute, как в пользовательском интерфейсеthread?

Поскольку Robolectric совместно использует один поток как для операций пользовательского интерфейса, так и для тестового кода, то мы должны использовать pollInSameThread, чтобы указать, что условие before должно быть оценено в том же потоке, что и тестовый пример, которыйначинается ожидание. Пример кода OP должен быть зафиксирован на:

await().pollInSameThread().until(() -> {
    flushBackgroundThreadScheduler()
    flushForegroundThreadScheduler()
    cf.isDone
});
...