Unit-Testing with Coroutines results in "You may not call the store from a thread other than the thread on which it was created"
jennymolske opened this issue ยท 8 comments
Hi,
we currently use your library in combination with Kotlin Coroutines and Flow. One of our middleware methods looks like this:
private fun agbLoad(store: Store<AppState>, action: AgbLoad, next: Dispatcher) {
CoroutineScope(Main).launch {
flow { emit(apiService.client().user().api().termsOfUse()) }
.flowOn(IO)
// flowOn only works upstream.
// Catch & Collect are executed on the main thread
.catch { e ->
e.printStackTrace()
store.dispatch(AgbLoadFailure(e))
}
.collect { agb -> store.dispatch(AgbLoadSuccess(LegalState.Agb(agb.text, agb.activeFrom.ddMMYYYY()))) }
}
next(action)
}
We wanted to test this (and the other functions) and wrote the following JUnit Test:
fun setUp() {
Dispatchers.setMain(mainThreadSurrogate)
recorderMiddleware = RecorderMiddleware()
legalMiddleware = LegalMiddleware(apiService(), assetFeatureMock, userServiceMock)
}
@Test
fun agbLoad_dispatchesAgbLoadSuccess() {
webServer().setDispatcher(object : Dispatcher() {
override fun dispatch(request: RecordedRequest?): MockResponse {
return MockResponse().setResponseCode(HttpURLConnection.HTTP_OK).setBody(termsOfUseResponseJson())
}
})
runBlocking {
CoroutineScope(Main).launch {
val store = createStore(appReducer, AppState(), applyMiddleware(
recorderMiddleware.all, LegalMiddleware(apiService(), assetFeatureMock, userServiceMock).all
))
store.dispatch(AgbLoad())
}
}
Barrier.awaitUntil { recorderMiddleware.getRecordings().size == 2 }
assertThat(recorderMiddleware.getRecordings().last(), instanceOf(AgbLoadSuccess::class.java))
}
@After
fun tearDown() {
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
mainThreadSurrogate.close()
}
Unfortunately the test throws an error:
Exception in thread "UI @coroutine#3" java.lang.IllegalStateException: You may not call the store from a thread other than the thread on which it was created. This includes: getState(), dispatch(), subscribe(), and replaceReducer() This store was created on: 'UI @coroutine#2' and current thread is 'UI @coroutine#3' at org.reduxkotlin.CreateStoreKt$createStore$3.invoke(CreateStore.kt:50) at org.reduxkotlin.CreateStoreKt$createStore$7.invoke(CreateStore.kt:174)
I already tried lots of coroutine combinations to get rid of this issue, but no success. Do you have an idea how we can fix this?
(additional information): The exception doesn't appear in production, only in the test scenario.
Thanks for your help :)
Interesting. Is this open source anywhere where I can pull it down? I will look into this.
It is not open source, but I've created a simplified project which also reproduces the error :)
You can download it from https://drive.google.com/open?id=1Cp1BYtFlFKiQd6X_cftLYA02XOMS-U0D
@patjackson52 Do you had time yet to look into this issue? :)
Thanks for pinging and the sample project @jennymolske. Looking into it now and working on a fix. It is a bug with 0.3.1. I would recommend going back to a previous version until I get the next release out (hopefully tomorrow or this weekend)
@jennymolske PR is up for this issue. If your interested please review. Will likely merge & release tomorrow if no issues.
fix available in v0.3.2
Great, thank you :)
If I change the version of the lib to 0.3.2 I get an error saying:
Failed to resolve: org.reduxkotlin:redux-kotlin-jvm:0.3.2
Do you know when the new version will be available?
It went live yesterday @jennymolske