Using this manual to test Coroutines. Writing a test that expected to throw exception crashes instead of passing the test. I wonder what i'm doing wrong.
private val testDispatcher = TestCoroutineDispatcher()
@Before
fun setup() {
// provide the scope explicitly, in this example using a constructor parameter
Dispatchers.setMain(testDispatcher)
}
@After
fun cleanUp() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
@Test(expected = RuntimeException::class)
fun testSomeFunctionWithException() = testDispatcher.runBlockingTest {
someFunctionWithException()
}
private fun someFunctionWithException() {
MainScope().launch {
throw RuntimeException("Failed via TEST exception")
}
}
The test method above and the one below
private val testScope = TestCoroutineScope()
private lateinit var subject: Subject
@Before
fun setup() {
// provide the scope explicitly, in this example using a constructor parameter
subject = Subject(testScope)
}
@After
fun cleanUp() {
testScope.cleanupTestCoroutines()
}
@Test(expected = RuntimeException::class)
fun testFooWithException() = testScope.runBlockingTest {
subject.fooWithException()
}
class Subject(private val scope: CoroutineScope) {
fun fooWithException() {
scope.launch {
println("fooWithException() thread: ${Thread.currentThread().name}")
throw RuntimeException("Failed via TEST exception")
}
}
}
both of them crash even though
Note: Prefer to provide TestCoroutineScope when it does not complicate code since it will also elevate exceptions to test failures.
TestCoroutineScope
uses TestCoroutineExceptionHandler
which will handle all the exceptions thrown in the coroutine collecting them in the uncaughtExceptions
list, although the first one will be rethrown during cleanUp
or more specifically when cleanupTestCoroutines()
is called, so you have to do something about that exception to prevent failing the tests.
@After
fun cleanUp() {
try {
testScope.cleanupTestCoroutines()
} catch (e: Exception) {
//Do something here
}
}
During the tests you can inspect the uncaughtExceptions
list in order to make your assertions:
@Test(expected = RuntimeException::class)
fun testFooWithException() = testScope.runBlockingTest {
subject.fooWithException()
assertEquals(1, uncaughtExceptions.size)
assertEquals(uncaughtExceptions[0].message, "Failed via TEST exception")
}
I do not have an environment where I can easily test the following, but try put your runBlocking
blocks inside the body of your test functions. For example:
@Test
fun myTest() {
runBlocking {
// Do your test stuff here
}
}
I have found that I have issues in test cases were I use the fun myTest() = runBlocking{
declaration before. It seems that the test runner is not able to detect the execution of the test with out some sort of assertion in the body, and where there is not a return. Something like that.
Any way, hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With