androidkotlinkotlin-coroutinesandroid-testingandroidx-test

runTest does not run on main thread


I am writing a test and I get this error from a standard library:

java.lang.IllegalStateException: Method addObserver must be called on the main thread

This is my simplified test method:

@Test
fun useAppContext() = runTest {
    assert(isMainThread())
}

How can I make this test succeed? I need to add an observer somewhere in my test which fails because I need to do so on the main thread.


Solution

  • Like runBlocking (and like code written directly in @Test methods), code within runTest is run on the test thread by default, rather than on the main thread. This is usually the behavior you want, because if you don't yield the main thread, it will not run layouts, and your UI will be completely frozen.

    To run a specific part of your test on the UI thread

    If you just have a specific part of your test that should be run on the UI thread, such as your example of the addObserver call, you can switch the coroutine dispatcher for that specific part of the test.

    Surround the relevant part of your method with withContext(Dispatchers.Main)

    @Test
    fun someTestMethod() = runTest {
        withContext(Dispatchers.Main) {
            assert(isMainThread())
        }
    }
    

    To run your entire test on the UI thread

    If you do want to run your test on the main thread (for example, because it's a simple test that doesn't involve any layouts, or because you want to manually control when you yield the main thread with suspend methods (such as Compose's withFrameNanos), this can be done with the @UiThreadTest annotation.

    Annotate the test method (or the class, if all @Test, @Before, and @After methods should be run on the UI thread) with @UiThreadTest.

    @Test
    @UiThreadTest
    fun someTestMethod() = runTest {
        assert(isMainThread())
    }