【发布时间】:2020-05-13 02:01:35
【问题描述】:
考虑这个类:
class TestViewModel(private val interactor: LoginInteractor) : ViewModel() {
private val _loading = MutableLiveData<Boolean>().apply { value = false }
val loading: LiveData<Boolean> = _loading
fun loginClicked() {
viewModelScope.launch {
_loading.value = true
val isLoggedIn = interactor.login()
_loading.value = false
}
}
}
interface LoginInteractor {
suspend fun login(): Boolean
}
和测试:
class TestViewModelTest {
@Rule
@JvmField
var rule = InstantTaskExecutorRule()
@Mock
private lateinit var interactor: LoginInteractor
@InjectMocks
private lateinit var tested: TestViewModel
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
Dispatchers.setMain(TestCoroutineDispatcher())
}
@Test
fun `should set loading to true while loading`() = runBlockingTest {
given(interactor.login()).willReturn(true)
tested.loginClicked()
Assert.assertTrue(tested.loading.value!!)
}
}
当断言loading 的值为true 时,显然它实际上不是。
当我们使用 RxJava 时,LoginInteractor 看起来像:
interface LoginInteractor {
fun login(): Single<Boolean>
}
在测试中,我们可以做到
given(interactor.login()).willReturn(Single.never())
在登录时测试ViewModel的状态;当调用loginClicked() 但interactor.login() 尚未返回时,如何延迟/不返回我的交互者suspend 函数login() 的任何内容来测试ViewModel 的状态?
【问题讨论】:
-
你在模拟可挂起的函数。意识到这一点的框架应该是提供函数永不返回选项的框架。但是,即使实现了这一点,您也不能只运行
loading标志被引发的断言。并发协程到达该状态可能需要任何时间。正确的测试将使用基于时间的循环,该循环会重试断言,直到为真或发生超时。
标签: android unit-testing kotlin coroutine kotlin-coroutines