Robolectric не может вызвать setValue в фоновом потоке

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

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

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

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()выполняются синхронно inline. Я могу наблюдать:

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

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

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

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

1 ответ

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

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

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

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

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

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

await().pollInSameThread().until(() -> {
    flushBackgroundThreadScheduler()
    flushForegroundThreadScheduler()
    cf.isDone
});
Другие вопросы по тегам