首页 > 解决方案 > 如何在挂起的协程函数之前测试代码

问题描述

在视图模型类中考虑以下示例:

    override fun signInViewSelected(
        email: String,
        password: String
    ) {
        viewModelScope.launch {
            loadingViewState.value = LoadingState.Loading("Loading")

            withContext(dispatcher.ioDispatcher) {
                authManager.signIn(email, password) // suspend fun signIn(email: String, password: String): Boolean -> makes network call
            }

            loadingViewState.value = LoadingState.NotLoading
        }
    }

我怎样才能测试这个方法,以便我可以验证我开始处于加载状态,调用 authManager.signIn 方法,然后最终处于未加载状态?

当我使用完成处理程序进行此设置时,我能够捕获传递给我的模拟authManager类的参数,然后手动调用它以推进完成,但是对于协程,我不熟悉如何执行等效行为。

我想要的是这样的,理想情况下:

    @Test
    fun `sign in loading state`() {
        signInViewModel.signInViewSelected("email@email.com", "password")

        val inProgressLoadingViewState = signInViewModel.loadingViewState.getOrAwaitValue()
        assertLoadingStateIsLoading(inProgressLoadingViewState, progressMessage)

        // delay mockAuthManager until now, have it execute at this point

        val finishedLoadingViewState = signInViewModel.loadingViewState.getOrAwaitValue()
        assertLoadingStateIsNotLoading(finishedLoadingViewState)
    }

有什么想法吗?

标签: unit-testingkotlincoroutine

解决方案


假设您熟悉 AssertJ 和 Mockito 之类的库,我将按照以下方式进行操作:

首先,模拟观察者loadingViewState(假设你有一些 LiveData)和authManager

private lateinit var viewModel: SomeViewModel
private val loadingViewStateObserver = mock<Observer<LoadingViewState>>()

fun initViewModel() {

    viewModel = SomeViewModel().apply {
        loadingViewStateLiveData.observeForever(loadingViewStateObserver)
    }
}
@Test
fun `sign in loading state`() {
    runBlocking {
        initViewModel()
        viewModel.signInViewSelected("email", "password")
    
        inOrder(loadingViewStateObserver, authManager) {
           verify(loadingViewStateObserver).onChanged(LoadingState.NotLoading)
           verify(authManager).signIn("email", "password")
           verify(loadingViewStateObserver).onChanged(LoadingState.Loading("Loading"))
        }
    }
}

推荐阅读