首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >考林克斯-协同试验。withContext {.}在liveData {.}完成后不会切换回以前的上下文

考林克斯-协同试验。withContext {.}在liveData {.}完成后不会切换回以前的上下文
EN

Stack Overflow用户
提问于 2020-06-22 15:47:47
回答 1查看 1K关注 0票数 1

我正在为我的Android应用程序编写一个集成测试。我在liveData {...}协同构建器中遇到了一个问题:当我在其中调用withContext {...}函数时,它会切换到给定的上下文(例如Dispatchers.IO),但在返回(到Dispatchers.Main.immediate)之后不会切换回来。

测试结果如下:

WordFragmentIntegrationTest:

代码语言:javascript
复制
@RunWith(RobolectricTestRunner::class)
@Config(application = TestAppWithDaggerComponent::class)
class WordFragmentIntegrationTest {

    @get:Rule
    val coroutinesRule = CoroutinesRule()

    private val mockWebServer = MockWebServer()

    @After
    fun teardown() {
        mockWebServer.shutdown()
    }

    @Test
    fun `should fetch a word from api and populate the view`() = runBlockingTest {
        // Mock api response, it works fine.
        val response = MockResponse().setResponseCode(HttpURLConnection.HTTP_OK)
                                     .setBody(SAMPLE_API_WORD_JSON)
                                     .setBodyDelay(0, TimeUnit.MILLISECONDS)
        mockWebServer.enqueue(response)
        mockWebServer.start(8080)

        // Launch a Fragment under test. It triggers an api call.
        val fragmentScenario = launchFragmentInContainer<WordFragment>()

        // Wait for the Fragment to do its work.
        fragmentScenario.onFragment { fragmentUnderTest ->
            runBlocking {
                while (fragmentUnderTest.isLoading) { yield() }
                
                // Do some assertions...
            }
        }
    }
}

CoroutinesRule:

代码语言:javascript
复制
class CoroutinesRule : ExternalResource() {

    override fun before() {
        Dispatchers.setMain(TestCoroutineDispatcher())
    }

    override fun after() {
        Dispatchers.resetMain()
    }
}

和一段代码,这会导致测试失败。

WordViewModel:

代码语言:javascript
复制
val wordLiveData = liveData {
    printCurrentThread("Emitting the first value")
    emit(UIState.ShowLoading)

    val value = withContext(Dispatchers.IO) {
        printCurrentThread("Fetching a value")
        loadWordUseCase(wordId)
    }

    printCurrentThread("Emitting the second value")
    emit(value)
}

private fun printCurrentThread(message: String) {
    val threadInfo = "Thread: ${Thread.currentThread().id}. UI thread: ${Looper.getMainLooper().thread.id}"
    println("$message. $threadInfo")
}

它在生产环境中运行良好:

代码语言:javascript
复制
Emitting the first value. Thread: 1. UI thread: 1  # First emitting is on the UI thread.
Fetching a value. Thread: 365. UI thread: 1        # Fetching is on some IO thread.
Emitting the second value. Thread: 1. UI thread: 1 # Switched back to the UI thread.

但是在Dispatchers.Main被替换的测试环境中,withContext {...}不会切换回liveData {...}Dispatchers.Main.immediate,它会导致CoroutineLiveData崩溃,因为它的emit()应该从UI线程中调用。

代码语言:javascript
复制
Emitting the first value. Thread: 11. UI thread: 11  # Emitted on the UI thread.
Fetching a value. Thread: 19. UI thread: 11          # Switched to IO thread.
Emitting the second value. Thread: 19. UI thread: 11 # Didn't switch back, which caused:

Exception in thread "DefaultDispatcher-worker-1 @coroutine#2"
 java.lang.IllegalStateException: Cannot invoke setValue on a background thread
    at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:462)
    at androidx.lifecycle.LiveData.setValue(LiveData.java:304)
...

是窃听器还是我做错了什么?也许我搞错了CoroutinesRule

EN

回答 1

Stack Overflow用户

发布于 2020-06-25 14:47:44

这是因为TestCoroutineDispatcher没有像android的Dispatchers.Main那样切换回特定的线程。相反,它的行为类似于Dispatchers.Unconfined

我不确定这是否适用于Robolectric,但是在单元测试中起作用的是:

InstantTaskExecutorRule添加到您的测试中,就像您对CoroutinesRule做的那样。例如:

代码语言:javascript
复制
@get:Rule
val executorRule = InstantTaskExecutorRule()

这将允许在任何线程上调用LiveData.setValue(...)

由于emit最终只是委托给setValue,所以不需要调整ViewModel代码。

如果该规则尚未可用,则添加此依赖项:

代码语言:javascript
复制
testImplementation 'androidx.arch.core:core-testing:2.1.0'

如果这仍然没有帮助,我想知道如果你不替换主调度员会发生什么

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62518374

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档