Aditya Wasan
03/09/2022, 11:34 AMpublic suspend fun saveAuthToken(authToken: String): Result<String, Throwable> {
return runCatching {
require(authToken.isNotEmpty()) { "authToken cannot be empty" }
val authTokenKey = stringPreferencesKey(AUTH_TOKEN_KEY)
// Use the external scope here to save the auth token
externalScope
.launch(ioDispatcher) { authDataStore.edit { store -> store[authTokenKey] = authToken } }
.join()
authToken
}
}
This is the code that launches a new coroutine in an external scope which will can live longer than the suspend function. Now, to test this behavior I’m assuming that I would have to pause the external coroutine and then cancel the scope in which the suspend function is called. After that, I can resume the external coroutine and then check the authDataStore to verify. However, I couldn’t find any standard way to do this. Does anyone know any resource or samples where I could look at a similar behavior. Thanks in advance.ephemient
03/09/2022, 11:35 AMAditya Wasan
03/09/2022, 11:37 AMephemient
03/09/2022, 11:41 AMAditya Wasan
03/09/2022, 11:46 AMyschimke
03/09/2022, 8:42 PMwithContext(NonCancellable) {
// this code will not be cancelled
}
Marcelo Hernandez
03/09/2022, 9:28 PMTestScope
instance for the externalScope
.
private val externalScope = TestScope(StandardTestDispatcher())
@Test
fun yourTest() = runTest { // the `this` TestScope != externalScope
…
}
Nick Allen
03/10/2022, 6:16 PMexternalScope
from your class that saves auth tokens means that your class (and its tests) can just focus on the DataStore. Create a separate class that represents an external scope executor. Something like:
class ExternalScopeExecutor(val scope: CoroutineScope) {
suspend fun <T> execute(block: suspend () -> T): T = scope.async { block() }.await()
}
Then you can test your ExternalScopeExecutor with something like this (which I think answers the core of your question):
@Test testExternalScope() = runTest {
var blockFinished = false
val dut = ExternalScopeExecutor(this)
val jobToCancel = launch {
dut.execute {
delay(10)
blockFinished = true
}
}
runCurrent() // Start the execute block
jobToCancel.cancel()
advanceUntilIdle() //Run any cancellation code finish the block
assertTrue(blockFinished)
}
`And then you can combine the functionality in another class and the tests for this other class will just check that Another class can combine them and the tests for this new class can mock/fake the AuthTokenSavingThing and ExternalScopeExecutor, just checking that saveAuthToken
is called inside a block passed to execute
.Aditya Wasan
03/14/2022, 8:22 AM