sjthn
06/09/2019, 5:09 PMfun onCreate() {
val user = profile.getUser()
if (!user.contains("token")) {
coroutineScope.launch(coroutineExceptionHandler) {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
try {
val token = network.fetchToken()
// ...
} finally {
withContext(Dispatchers.Main) {
view.hideProgressBar()
}
}
}
}
} else {
// ...
}
}
I wrote a test function to verify if network.fetchToken()
is called when condition is met. I wrapped it in runBlocking
but test fails. Any ideas on how to test this?gildor
06/10/2019, 2:59 AMgildor
06/10/2019, 2:59 AMgildor
06/10/2019, 3:02 AMgildor
06/10/2019, 3:02 AMgildor
06/10/2019, 3:02 AMcoroutineScope.launch(coroutineExceptionHandler) {
launch(<http://Dispatchers.IO|Dispatchers.IO>) {
gildor
06/10/2019, 3:02 AMcoroutineScope.launch(coroutineExceptionHandler + <http://Dispatchers.IO|Dispatchers.IO>) {
gildor
06/10/2019, 3:03 AMfetchToken
blocking?sjthn
06/10/2019, 5:21 AMfetchToken
is a network callgildor
06/10/2019, 5:22 AMgildor
06/10/2019, 5:22 AM<http://Dispatchers.IO|Dispatchers.IO>
sjthn
06/10/2019, 5:23 AMfetchToken
is a suspend function. that calls the api and returns the result synchronouslysjthn
06/10/2019, 5:24 AMgildor
06/10/2019, 5:25 AMgildor
06/10/2019, 5:27 AMsjthn
06/10/2019, 5:27 AMgildor
06/10/2019, 5:28 AM<http://Dispatchers.IO|Dispatchers.IO>
, Retrofit requests running on OkHttp dispatcher by defaultgildor
06/10/2019, 5:28 AMsjthn
06/10/2019, 5:31 AMsjthn
06/10/2019, 5:33 AMDispatchers.setMain()
and resetMain()
in my test classgildor
06/10/2019, 5:33 AMi thought we anyway need to call it in a non-ui threadSee, suspend function is like a callback with some compile time magic, same way as with Retrofit callbacks you do not switch thread where to run it, you shouldn’t do this for suspend function if it’s not required
gildor
06/10/2019, 5:33 AMI run all the tests some are failing.I don’t know what your tests are testing, so hard to say anything, try to investigate
gildor
06/10/2019, 5:34 AMgildor
06/10/2019, 5:35 AMgildor
06/10/2019, 5:35 AMsjthn
06/10/2019, 5:37 AMgildor
06/10/2019, 5:38 AMThe testing part seems difficultBecause your function is written in a way which is very hard to test, it would be the same with any async API if you write your code this way. Essentially when you call this function you just run background job and have no way to await or know result, you just have some side effect but to check it you should wait for this job, which is not easy in this case. Of course you can replace of dispatchers for tests with Unconfined and it will work, but pretty fragile (if any of functions switches context your test will be broken)
gildor
06/10/2019, 5:39 AMsjthn
06/10/2019, 5:49 AM@Test
fun `make api call if token is not present`() {
`when`(profile.getUser()).thenReturn("")
runBlocking {
presenter.onCreate()
verify(network).fetchToken()
}
}
gildor
06/10/2019, 5:49 AMgildor
06/10/2019, 5:50 AMgildor
06/10/2019, 5:50 AMgildor
06/10/2019, 5:51 AMgildor
06/10/2019, 5:52 AMgildor
06/10/2019, 5:52 AMgildor
06/10/2019, 5:55 AMsjthn
06/10/2019, 6:07 AMInstead, you can extract this to some suspend function, do request, receive result and then validate that token was requested (or even better returned together with User)But I am doing that only IIUIC. Extracted the n/w request to a suspend function that returns the token. So checking whether that token was requested i.e, the function was called or not
gildor
06/10/2019, 6:10 AMgildor
06/10/2019, 6:10 AMgildor
06/10/2019, 6:10 AMgildor
06/10/2019, 6:11 AMgildor
06/10/2019, 6:11 AMsjthn
06/10/2019, 6:12 AMrook
06/10/2019, 5:49 PMview
, for example)sjthn
06/11/2019, 8:03 AM