I am trying to use coroutines to handle asynchronous code for my login service. Unfortunately, the implementation of the login service must accept callbacks when it completes. I do not want this login()
function to complete until one of these callbacks occurs.
Here is what I have:
fun login(): Outcome = runBlocking {
suspendCoroutine<Outcome> { continuation ->
loginService.login(
onLoginSuccess = {
// do some stuff
continuation.resume(Outcome.SUCCESS)
},
onLoginFailure = {
// handle failure case
continuation.resume(Outcome.FAILURE)
}
)
}
}
My issue is my tests never complete. I think what is happening is that the continuation block itself isn't running. I tried wrapping the call to uut.login()
in a runBlocking as well, but it didn't help. Here is my test code (using Spek):
describe("when login") {
val successCaptor: ArgumentCaptor<() -> Unit> = TestHelpers.argumentCaptorForClass()
val failureCaptor: ArgumentCaptor<() -> Unit> = TestHelpers.argumentCaptorForClass()
var result: Outcome? = null
beforeEachTest {
doNothing().whenever(mockLoginService)?.login(capture(successCaptor), capture(failureCaptor))
result = uut?.execute()
}
it("logs in with the login service") {
verify(mockLoginService)?.login(any(), any())
}
describe("and the login succeeds") {
beforeEachTest {
successCaptor.value.invoke()
}
// other tests...
it("returns an outcome of SUCCESS") {
expect(result).to.equal(Outcome.SUCCESS)
}
}
describe("and the login fails") {
beforeEachTest {
failureCaptor.value.invoke()
}
// other tests...
it("returns an outcome of FAILURE") {
expect(result).to.equal(Outcome.FAILURE)
}
}
}
Basically, I'd like to assert that the login()
method returned either a SUCCESS or FAILURE outcome based on what occurred.
Any ideas?
Of course, I figured this out right after posting. If interested, here is what I did in the test:
describe("when login") {
val successCaptor: ArgumentCaptor<() -> Unit> = TestHelpers.argumentCaptorForClass()
val failureCaptor: ArgumentCaptor<() -> Unit> = TestHelpers.argumentCaptorForClass()
var result: Outcome? = null
describe("and the login succeeds") {
beforeEachTest {
whenever(mockLoginService?.login(capture(successCaptor), capture(failureCaptor))).thenAnswer {
successCaptor.value.invoke()
}
result = uut?.execute()
}
it("logs in with the login service") {
verify(mockLoginService)?.login(any(), any())
}
it("returns an outcome of SUCCESS") {
expect(result).to.equal(Outcome.SUCCESS)
}
}
describe("and the login fails") {
beforeEachTest {
whenever(mockLoginService?.login(capture(successCaptor), capture(failureCaptor))).thenAnswer {
failureCaptor.value.invoke()
}
result = uut?.execute()
}
it("logs in with the login service") {
verify(mockLoginService)?.login(any(), any())
}
// other tests
it("returns an outcome of FAILURE") {
expect(result).to.equal(Outcome.FAILURE)
}
}
}