android - 如何让 MockWebServer + Retrofit + Coroutines 在同一个调度器中运行

标签 android retrofit2 kotlin-coroutines mockwebserver

我在我的 Android 单元测试中尝试使用 MockWebServer + Retrofit + Coroutines 失败。在调试过程中,我发现 OkHttp 在不同的线程上运行,这就是为什么我的测试总是失败。

这是我设置测试调度程序的规则:

class MainCoroutineRule(
    private val scheduler: TestCoroutineScheduler = TestCoroutineScheduler(),
    val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(scheduler)
) : TestWatcher() {

    val testScope = TestScope(testDispatcher)

    override fun starting(description: Description) {
        super.starting(description)
        Dispatchers.setMain(testDispatcher)
    }

    override fun finished(description: Description) {
        super.finished(description)
        Dispatchers.resetMain()
    }
}

这是我的 View 模型:

@HiltViewModel
class TestViewModel @Inject constructor(
    private val repository: TestRepository,
    @MainDispatcher private val dispatcher: CoroutineDispatcher
) : ViewModel() {

    val hasData = MutableLiveData(false)

    fun fetchSomething() {
        viewModelScope.launch(dispatcher) {

            when (repository.getSomething()) {
                is Success -> {
                    hasData.value = true
                }
                else -> {
                    hasData.value = false
                }
            }
        }
    }
}

最后是测试:

class TestViewModelTest : MockServerSuite() {
    @get:Rule
    val taskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    val mainTestRule = MainCoroutineRule()

    @Test
    fun `my test`() = mainTestRule.testScope.runTest {
        // setting up MockWebServer

        val viewModel = TestViewModel(
            repository = TestRepository(
                api = testApi(server),
            ),
            dispatcher = mainTestRule.testDispatcher
        )

        viewModel.fetchSomething()
        assertThat(viewModel.hasData.value).isTrue
    }
}

由于 Retrofit 是主安全的,我不知道如何让它在我的 testDispatcher 上运行。我错过了什么吗?

正如 Petrus 提到的,我已经使用了 getOrAwaitValue,它适用于简单的场景。 在我们的大多数用例中,我们的请求在收到一些数据后会触发其他请求。 通常,我收到 getOrAwaitValue 的中间值,而不是我期望的最终 LiveData。

最佳答案

尝试使用 getOrAwaitValue :)

class TestViewModelTest {
    @Test
    fun `my test`() = mainTestRule.testScope.runTest {
        // setting up MockWebServer

        val viewModel = TestViewModel(
            repository = TestRepository(
                api = testApi(server),
            ),
            dispatcher = mainTestRule.testDispatcher
        )

        viewModel.fetchSomething()
        assertThat(viewModel.hasData.getOrAwaitValue()).isTrue
    }
}

@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

关于android - 如何让 MockWebServer + Retrofit + Coroutines 在同一个调度器中运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71489875/

相关文章:

android - 无法检测到 Android NDK 工具链

kotlin - Kotlin中的两个协程启动之间的差异

kotlin - 在流的收集中获取当前和以前的值

android - 如何在 Android 中使用 Retrofit 2.0 处理无网络连接?

java - 如何像Javascript中的Promise一样检测Retrofit上的所有异步操作是否完成?

java - 从工件的 .jar 运行时找不到 Kotlinx 协程类

android - 在 RetrofitError 中捕获错误并将其传递给 RoboSpice Error

android - ionic 3:无法加载资源:net::ERR_FILE_NOT_FOUND logo.png

Android GCM 消息重复

java - Retrofit 2,如何在其他 Activity 中访问收到的ArrayList?