TIL: Don't mix blockingGet() and Main Looper

RxJava で main thread になんかさせるコードが関与する場合 Observable#blockingFirst() を使うとデッドロックしてしまう。そこで以下のようなかんじで subscription と retrieval を分離し、その間でタスクを flush する。

AtomicReference<String> result = new AtomicReference();
Disposable unused = yourSubject.subscribe(result::set);
shadowOf(getMainLooper()).idle();
assertThat(result.get()).isEqualTo("Expected");

  • RxJava のコードには BlockingObsever というのがあるのでこれを使わせてくれれば解決なのだが、使えないので雑に AtomicReference で済ます。
  • unit test 内でスレッドのタスクキューを空にする方法は色々あるっぽい。上の例は Robolectric のこの資料をもとにしている。この方法では background threads は待てない。
  • Coroutine だとどうするんだろうね?ていうかテストフレームワークがいい加減非同期対応してほしいもんだわ JS とか 10 年前から対応してんだぞ。

追記

Coroutine はサポートがあるらしい。