'coroutine nested exception problem on unit test

Can somebody explain why the exception problem is happening only in unit test? I was testing exceptional case for flow and I found exception does not throw as I expected.

val dispatcher = TestCoroutineDispatcher()
try {
  dispatcher.runBlockingTest {
    flow<Resource<Int>> {
      println("1. body thread: ${Thread.currentThread().name}")
      throw FlowFailureException()
    }
    .catch { e ->
      emit(Resource.Error(RootFlowFailureException(cause = e)))
    }
    .flowOn(Dispatchers.Default)
    .map {
      println("2. map thread: ${Thread.currentThread().name}")
      when (it) {
        is Resource.Success<Int> -> it.data
        is Resource.Error -> {
          throw IllegalStateException(it.exception)
        }
      }
    }
    .collect()
  }
} catch (e: Throwable) {
   println("catching exception: ${e.javaClass}")
   e.printAll()
   assertThat(e.javaClass, equalTo(IllegalStateException::class.java))
   // problem happened here
   assertThat(e.cause?.javaClass, equalTo(RootFlowFailureException::class.java))
   assertThat(e.cause?.cause?.javaClass, equalTo(FlowFailureException::class.java))
   assertThat(e.cause?.cause?.cause, `is`(nullValue()))
}


sealed class Resource<out R> {
    data class Success<out T>(val data: T) : Resource<T>()
    data class Error(val exception: Throwable) : Resource<Nothing>()

    override fun toString(): String {
        return when (this) {
            is Success<*> -> "Success[data=$data]"
            is Error -> "Error[exception=$exception]"
        }
    }
}

class RootFlowFailureException(
    message: String? = null,
    cause: Throwable? = null
) : RuntimeException(message, cause)

class FlowFailureException(
    message: String? = null,
    cause: Throwable? = null
) : RuntimeException(message, cause)

# system logs
System.out: 1. body thread: DefaultDispatcher-worker-1 @coroutine#2
System.out: 2. map thread: Test worker @coroutine#1
System.out: catching exception: class java.lang.IllegalStateException
System.out:  e> class java.lang.IllegalStateException
System.out:  e> class java.lang.IllegalStateException
System.out:  e> class org.test.FlowOperatorTest$RootFlowFailureException
System.out:  e> class org.test.FlowOperatorTest$FlowFailureException

I expected exception hierarchy as follows:

IllegalStateException -> RootFlowFailureException -> FlowFailureException

But actual exception hierarchy was:

IllegalStateException -> IllegalStateException -> RootFlowFailureException -> FlowFailureException

This case doesn't happen in ViewModel (real implementation) and it only happened in unit test. So I assume that it is because of TestCoroutineDispatcher or TestCoroutineExceptionHandler that handles exception or coroutine.

Does anyone have idea to this issue?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source