Eri*_*rik 9 android unit-testing android-testing android-viewmodel android-architecture-components
这是我的 MWE 测试类,它依赖于 AndroidX、JUnit 4 和 MockK 1.9:
class ViewModelOnClearedTest {
@Test
fun `MyViewModel#onCleared calls Object#function`() = mockkObject(Object) {
MyViewModel::class.members
.single { it.name == "onCleared" }
.apply { isAccessible = true }
.call(MyViewModel())
verify { Object.function() }
}
}
class MyViewModel : ViewModel() {
override fun onCleared() = Object.function()
}
object Object {
fun function() {}
}
Run Code Online (Sandbox Code Playgroud)
注意:该方法在超类中受保护ViewModel。
我想验证MyViewModel#onCleared调用Object#function. 上面的代码通过反射实现了这一点。我的问题是:我可以以某种方式运行或模拟 Android 系统以便onCleared调用该方法,这样我就不需要反射吗?
来自onClearedJavaDoc:
当这个 ViewModel 不再使用时会调用这个方法,并且会被销毁。
那么,换句话说,我如何创建这种情况,以便我知道onCleared被调用并且我可以验证它的行为?
小智 13
我刚刚创建了 ViewModel 的扩展:
/**
* Will create new [ViewModelStore], add view model into it using [ViewModelProvider]
* and then call [ViewModelStore.clear], that will cause [ViewModel.onCleared] to be called
*/
fun ViewModel.callOnCleared() {
val viewModelStore = ViewModelStore()
val viewModelProvider = ViewModelProvider(viewModelStore, object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T = this@callOnCleared as T
})
viewModelProvider.get(this@callOnCleared::class.java)
//Run 2
viewModelStore.clear()//To call clear() in ViewModel
}
Run Code Online (Sandbox Code Playgroud)
在 kotlin 中,您可以使用覆盖受保护的可见性public,然后从测试中调用它。
class MyViewModel: ViewModel() {
public override fun onCleared() {
///...
}
}
Run Code Online (Sandbox Code Playgroud)
在这个答案中,Robolectric 用于让 Android 框架onCleared在您的ViewModel. 这种测试方式比使用反射慢(如问题中所示),并且取决于 Robolectric 和 Android 框架。这种权衡取决于你。
...你可以看到ViewModel#onCleared它只被调用ViewModelStore(为你自己的ViewModels)。这是视图模型的存储类,由ViewModelStoreOwner类拥有,例如FragmentActivity. 那么,什么时候ViewModelStore调用onCleared你的ViewModel?
它必须存储您的ViewModel,然后必须清除存储(您不能自己做)。
您的视图模型由您使用ViewModelProvider时存储,您的视图模型类在哪里。它存储在的。getViewModelViewModelProviders.of(FragmentActivity activity).get(Class<T> modelClass)TViewModelStoreFragmentActivity
例如,当您的 Fragment 活动被销毁时,商店就很清楚了。这是一堆遍布各处的连锁调用,但基本上是:
FragmentActivity。ViewModelProvider使用ViewModelProviders#of。ViewModel使用ViewModelProvider#get。现在,onCleared应该在您的视图模型上调用。让我们使用 Robolectric 4、JUnit 4、MockK 1.9 对其进行测试:
@RunWith(RobolectricTestRunner::class)到您的测试类。Robolectric.buildActivity(FragmentActivity::class.java)setup在控制器上使用初始化活动,这允许它被销毁。get方法获取活动。destroy在控制器上使用销毁活动。onCleared。...基于问题的例子:
@RunWith(RobolectricTestRunner::class)
class ViewModelOnClearedTest {
@Test
fun `MyViewModel#onCleared calls Object#function`() = mockkObject(Object) {
val controller = Robolectric.buildActivity(FragmentActivity::class.java).setup()
ViewModelProviders.of(controller.get()).get(MyViewModel::class.java)
controller.destroy()
verify { Object.function() }
}
}
class MyViewModel : ViewModel() {
override fun onCleared() = Object.function()
}
object Object {
fun function() {}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3620 次 |
| 最近记录: |