【问题标题】:Test a view model with livedata, coroutines (Kotlin)使用 livedata、协程 (Kotlin) 测试视图模型
【发布时间】:2020-04-12 20:03:40
【问题描述】:

几天来我一直在尝试测试我的视图模型,但没有成功。

这是我的视图模型:

class AdvertViewModel : ViewModel() {

    private val parentJob = Job()

    private val coroutineContext: CoroutineContext
        get() = parentJob + Dispatchers.Default

    private val scope = CoroutineScope(coroutineContext)

    private val repository : AdvertRepository = AdvertRepository(ApiFactory.Apifactory.advertService)

    val advertContactLiveData = MutableLiveData<String>()

    fun fetchRequestContact(requestContact: RequestContact) {
        scope.launch {
            val advertContact = repository.requestContact(requestContact)
            advertContactLiveData.postValue(advertContact)
        }
    }
}

这是我的存储库:

class AdvertRepository (private val api : AdvertService) : BaseRepository() {

    suspend fun requestContact(requestContact: RequestContact) : String? {

        val advertResponse = safeApiCall(
            call = {api.requestContact(requestContact).await()},
            errorMessage = "Error Request Contact"
        )

        return advertResponse
    }
}

这是我的视图模型测试:

@RunWith(JUnit4::class)
class AdvertViewModelTest {
    private val goodContact = RequestContact(...)

    private lateinit var advertViewModel: AdvertViewModel

    private var observer: Observer<String> = mock()

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setUp() {
        advertViewModel = AdvertViewModel()
        advertViewModel.advertContactLiveData.observeForever(observer)
    }

    @Test
    fun fetchRequestContact_goodResponse() {
        advertViewModel.fetchRequestContact(goodContact)

        val captor = ArgumentCaptor.forClass(String::class.java)

        captor.run {
            verify(observer, times(1)).onChanged(capture())
            assertEquals("someValue", value)
        }
    }
}

mock()方法:

inline fun <reified T> mock(): T = Mockito.mock(T::class.java)

我收到了这个错误:

需要但未调用:observer.onChanged(); -> 在 com.vizzit.AdvertViewModelTest.fetchRequestContact_goodResponse(AdvertViewModelTest.kt:52) 实际上,与此模拟的交互为零。

我不明白如何检索我的查询结果。

【问题讨论】:

    标签: kotlin retrofit2 viewmodel android-livedata coroutine


    【解决方案1】:

    您需要编写一个OneTimeObserver 来观察来自ViewModel 的实时数据

    class OneTimeObserver<T>(private val handler: (T) -> Unit) : Observer<T>, LifecycleOwner {
        private val lifecycle = LifecycleRegistry(this)
    
        init {
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
        }
    
        override fun getLifecycle(): Lifecycle = lifecycle
    
        override fun onChanged(t: T) {
            handler(t)
            lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        }
    }
    

    之后你可以写一个扩展函数:

    fun <T> LiveData<T>.observeOnce(onChangeHandler: (T) -> Unit) {
        val observer = OneTimeObserver(handler = onChangeHandler)
        observe(observer, observer)
    }
    

    您可以检查我从项目中获得的this ViewModel class 类,以在您执行(何时)调用方法后检查您的LiveData 发生了什么。

    至于你的错误,它只是说 onChanged() 方法永远不会被调用。

    【讨论】:

    • 谢谢我正在尝试实施您的解决方案,我在哪里可以找到 AppCoroutineDispatchers?
    • 哦,请忽略它。因为它只是一个具有Dispatchers.IODispatchers.Main 的数据类,但如果您不使用协程,则根本不需要它。
    • 好吧,我照你说的做了,但什么也没发生,我的测试总是绿色的:ctxt.io/2/AABArrklFA
    • 如果是绿色,则表示测试通过。
    • 为了避免由于未调用断言而导致始终绿色测试的情况,我可以建议添加变量 var isCaptorCalled = false,在 captor 中设置为 true。在测试结束时,您需要额外的测试assertTrue(isCaptorCalled),如果没有调用您的捕获者,这将使您的测试崩溃。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-30
    • 2020-03-27
    • 1970-01-01
    • 2019-11-23
    • 2020-05-07
    相关资源
    最近更新 更多