I've started using kotlin coroutines in my Android project recently, but I have somewhat of a problem with it. Many would call it a code smell.
I'm using an MVP architecture where the coroutines are started in my presenter like this:
// WorklistPresenter.kt
...
override fun loadWorklist() {
...
launchAsync { mViewModel.getWorklist() }
...
The launchAsync
function is implemented this way (in my BasePresenter class that my WorklistPresenter class extends):
@Synchronized
protected fun launchAsync(block: suspend CoroutineScope.() -> Unit): Job {
return launch(UI) { block() }
}
The problem with this is that I'm using a UI coroutine context that depends on the Android Framework. I can't change this to another coroutine context without running into ViewRootImpl$CalledFromWrongThreadException
. To be able to unit test this I've created a copy of my BasePresenter with a different implementation of launchAsync
:
protected fun launchAsync(block: suspend CoroutineScope.() -> Unit): Job {
runBlocking { block() }
return mock<Job>()
}
To me this is a problem because now my BasePresenter has to be maintained in two places. So my question is. How can I change my implementation to support easy testing?
I’d recommend to extract the launchAsync
logic into a separate class, which you can simply mock in your tests.
class AsyncLauncher{
@Synchronized
protected fun execute(block: suspend CoroutineScope.() -> Unit): Job {
return launch(UI) { block() }
}
}
It should be part of your activity constructor in order to make it replaceable.