reduxkotlin / redux-kotlin

Redux implementation for Kotlin (supports multiplatform JVM, native, JS, WASM)
https://reduxkotlin.org
MIT License
426 stars 32 forks source link

Unit-Testing with Coroutines results in "You may not call the store from a thread other than the thread on which it was created" #38

Closed jennymolske closed 4 years ago

jennymolske commented 4 years ago

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 :)

patjackson52 commented 4 years ago

Interesting. Is this open source anywhere where I can pull it down? I will look into this.

jennymolske commented 4 years ago

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

jennymolske commented 4 years ago

@patjackson52 Do you had time yet to look into this issue? :)

patjackson52 commented 4 years ago

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)

patjackson52 commented 4 years ago

@jennymolske PR is up for this issue. If your interested please review. Will likely merge & release tomorrow if no issues.

patjackson52 commented 4 years ago

fix available in v0.3.2

jennymolske commented 4 years ago

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?

patjackson52 commented 4 years ago

It went live yesterday @jennymolske