I am testing an AsyncTask
that onPostExecute
calls setValue
of a LiveData
instance. Since I am invoking setValue
from onPostExecute
no issues were expected regarding the invocation being done by the UI thread.
Yet running this in a Robolectric unit test I got: java.lang.IllegalStateException: Cannot invoke setValue on a background thread
To make this unit test wait for background and foreground tasks completion I take advantage of awaitility tool in the following way:
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
});
This is throwing an IllegalStateException: Cannot invoke setValue on a background thread
on flushForegroundThreadScheduler()
call!!!!
Why I am getting this exception? And how can I have the onPostExecute
being performed like in the UI thread?
UPDATE
Logging threads it seems that both flushBackgroundThreadScheduler()
and flushForegroundThreadScheduler()
are executed synchronously inline. I can observe:
LiveData created on thread 763324286
Background thread 1519527121
LiveData updated on thread 1519527121
Since the lambda passed to await.until
runs on another thread, then both flushBackgroundThreadScheduler()
and flushForegroundThreadScheduler()
are performed on that thread 1519527121.
Thus, I can solve my problem with the following workaround running in the test thread corresponding to UI Thread. Yet, I need that Thread.sleep()
to succeed and I don't like it.
Thread.sleep(1000)
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone
We must have the following considerations into account regarding:
flushForegroundThreadScheduler()
is executed synchronously inline.await.until(<Callable>)
evaluates the until condition in a background thread.From these two statements we observe that invoking
flushForegroundThreadScheduler()
from theCallable
passed toawait.until(<Callable>)
results in the invocation of scheduled foreground tasks in a background thread, which answers the first OP question: Why I am getting this exception?Answering the second OP question:
Since Robolectric shares a single thread for both UI operations and Test code, then we must use
pollInSameThread
to instruct that the until condition should be evaluated on the same thread as the test case that starts Awaitility. The OP sample code should be fixed to: