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 ответ
Мы должны принять во внимание следующие соображения относительно:
- Робоэлектрик:
flushForegroundThreadScheduler()
выполняется синхронно inline. - Ожидание:
await.until(<Callable>)
оценивает условие до в фоновом потоке.
Из этих двух утверждений мы видим, что обращение к flushForegroundThreadScheduler()
из Callable
перешел к await.until(<Callable>)
приводит к вызову запланированных задач переднего плана в фоновом потоке, что отвечает на первый вопрос OP: Почему я получаю это исключение?
Отвечая на второй вопрос OP:
как я могу получить
onPostExecute
выполняется как в потоке пользовательского интерфейса?
Поскольку Robolectric использует один поток как для операций пользовательского интерфейса, так и для тестового кода, мы должны использовать pollInSameThread
чтобы указать, что условие пока должно оцениваться в том же потоке, что и тестовый пример, запускающий Awaitility. Пример кода OP должен быть исправлен на:
await().pollInSameThread().until(() -> {
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone
});