Robolectric无法在后台线程上调用setValue



我正在测试AsyncTaskonPostExecute调用LiveData实例的setValue。由于我是从onPostExecute调用setValue,所以对于UI线程进行的调用,预计不会出现任何问题。

然而,在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
});

这是在flushForegroundThreadScheduler()调用上抛出IllegalStateException: Cannot invoke setValue on a background thread

为什么我会得到这个例外?我如何才能像在UI线程中那样执行onPostExecute

更新

记录线程似乎flushBackgroundThreadScheduler()flushForegroundThreadScheduler()都是以内联方式同步执行的。我可以观察到:

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

由于传递给await.until的lambda在另一个线程上运行,因此flushBackgroundThreadScheduler()flushForegroundThreadScheduler()都在该线程1519527121上执行。

因此,我可以通过在与UI线程对应的测试线程中运行以下变通方法来解决我的问题。然而,我需要Thread.sleep()才能成功,我不喜欢它

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

我们必须考虑以下因素:

  1. Robolectric:flushForegroundThreadScheduler()以内联方式同步执行
  2. 等待性:await.until(<Callable>)在后台线程中评估直到条件

从这两个语句中,我们观察到从传递给await.until(<Callable>)Callable调用flushForegroundThreadScheduler()会导致在后台线程中调用调度的前台任务,这回答了第一个OP问题:为什么我会得到这个异常

回答OP的第二个问题:

如何像在UI线程中那样执行onPostExecute

由于Robolectric为UI操作和测试代码共享一个线程,因此我们必须使用pollInSameThread来指示应该在与启动Awaitility的测试用例相同的线程上评估until条件。OP示例代码应固定为:

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

相关内容

  • 没有找到相关文章

最新更新