Has anybody had ever written timeout tests for Ret...
# coroutines
a
Has anybody had ever written timeout tests for Retrofit with MockWebServer + coroutines? I’m getting
This job has not completed yet
on the following test:
Copy code
@Test
    fun requestWithTimeout_serverRespondsInTime_returnsResponse() = coroutinesRule.testDispatcher.runBlockingTest {
        val body = "{ field: Test }"
        val response = MockResponse()
            .setResponseCode(HttpURLConnection.HTTP_OK)
            .setBody(body)
            .throttleBody(body.length.toLong() / 2, 9, TimeUnit.SECONDS)

        mockWebServer.enqueue(response)

        val actual = service.requestWithTimeout(20_000)

        assertEquals(ResponseTest("Test"), actual)
    }
👍 1
j
You can look at the built-in tests
Your problem is the use of runBlockingTest which I would advise against using in general, but absolutely won't work here.
u
why
a
I was using
runBlocking
and the test passes but it takes too long to complete
j
MWS doesn't honor fake time so you have no choice if this is truly what you want to test.
👍 2
We've talked about having a fake clock as part of Okio in the past
u
@jw if I might OT, whats wrong with runBlockingTest?
j
Well for one it doesn't work with anything implemented using suspendCoroutine such as Retrofit or anything callback-based.
u
what then, should I try to make the supsend functions actually blocking in tests?
j
I use normal runBlocking and TestDispatcher for fake time
👍 1
u
do you use it as well when fake time is not needed? or swap for Unconfined then? or not care since its runBlocking, and just swap Main if its a view model-thing?
j
Just normal runBlocking otherwise. Turbine means we don't need to swap out dispatchers since it will wait for events and if you're referring to AndroidX ViewModels then we don't use those.
u
no, just general ui-level business layer, presenter or what have you, which needs android main thread, so dispatchers.main in its scope
j
our UI layer doesn't have coroutines. the view-layer is basically a non-suspending setModel function call. presenters are Rx/Flow streams in, Rx/Flow streams out and they inject the emission scheduler/dispatcher so in the case of Flow it doesn't matter what it used it will still work.
u
right but the presenter needs to subscribe some upstreams of which data you want to display, transform it, maybe like this
Copy code
Presenter(someRepository: Repository) {
	val state: Flow<State> get() = _state
	private val _state = MutableStateFlow()
	
	init {
		scope.launch {
			someRepository.whatever
                .someTransforms
				.collect {
					_state.value = it
				}
		}
	}

    fun someClick() {
		scope.launch {
			_state.value = Loading
			someRepository.doSomething()
			_state.value = NotLoading
		}
	}
}
or are they jsut transformations, and not keep state?
j
Ah yeah I see. Right now, for better or worse, they are pure transformations where all state is captured in the stream in some form. I would like to change that and enable something like you describe where local state is used and you can write more imperative code rather than capturing all the logic in Rx/Flow operators.
u
Yes, and the question is how should the scope instance look like, for now in my code it looks like this
Copy code
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.main)
or rather the disaptcher instance is injected via ctor and the question is what should one replace it with during tests. I was used to rx and its Schedulers.trampoline() and keeping everything blocking; but MutableStateFlow doesnt seem to work the same when used with Dispatchers.Unconfined