【问题标题】:Let suspend function not return anything让挂起函数不返回任何东西
【发布时间】: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


【解决方案1】:

选项 1:

为了保证你的状态发生变化,你可以订阅加载状态的变化:

@Test
 fun `should set loading to true while loading`() = runBlockingTest {
     given(interactor.login()).willReturn(true)

     var invocationCounter = 0
     tested.loading.observerForever { loading -> 
         assertEquals(invocationCounter == 0, loading)
         invocationCounter++
     }

     tested.loginClicked()

     Assert.assertTrue(tested.loading.value!!)
 }

选项 2:

   ```
     given(interactor.login()).willAnswer {
         assertTrue(tested.loading.value)
         true
    }

    tested.loginClicked()

    Assert.assertFalse(tested.loading.value!!)

【讨论】:

  • 您选择了哪个选项? :)
  • 目前排名第二,似乎更直观。
猜你喜欢
  • 2016-11-10
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多