这是我的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() {}
}
请注意:该方法在超类ViewModel
中受保护。
我想验证MyViewModel#onCleared
是否呼叫Object#function
。上面的代码是通过反射实现的。我的问题是:我可以以某种方式运行或模拟Android系统,以便调用onCleared
方法,这样就不需要反射了吗?
从onCleared
JavaDoc:
当不再使用此ViewModel时,将调用此方法。
换句话说,如何创建这种情况以使我知道onCleared
被呼叫并且可以验证其行为?
答案 0 :(得分:6)
在kotlin中,我发现可以使用public
覆盖受保护的可见性,然后可以从测试中调用它。
class MyViewModel: ViewModel() {
public override fun onCleared() {
///...
}
}
答案 1 :(得分:0)
在此答案中,Robolectric用于让Android框架在您的onCleared
上调用ViewModel
。这种测试方法比使用反射(像在问题中一样)要慢,并且取决于Robolectric和Android框架。权衡由您决定。
...您可以看到ViewModel#onCleared
仅在ViewModelStore
中被调用(对于您自己的ViewModels
)。这是视图模型的存储类,由ViewModelStoreOwner
类拥有,例如FragmentActivity
。那么,ViewModelStore
何时在您的onCleared
上调用ViewModel
?
它必须存储您的ViewModel
,然后必须清除存储(您不能自己做)。
当您使用ViewModelProvider
get
ViewModel
时,ViewModelProviders.of(FragmentActivity activity).get(Class<T> modelClass)
存储您的视图模型,其中T
是您的视图模型类。它将其存储在ViewModelStore
的{{1}}中。
例如,当您的片段活动被销毁时,商店就很清楚。到处都是一连串的链接呼叫,但基本上是:
FragmentActivity
。FragmentActivity
获取其ViewModelProvider
。ViewModelProviders#of
获取ViewModel
。现在,应该在视图模型上调用ViewModelProvider#get
。让我们使用Robolectric 4,JUnit 4,MockK 1.9进行测试:
onCleared
添加到测试班。@RunWith(RobolectricTestRunner::class)
Robolectric.buildActivity(FragmentActivity::class.java)
初始化活动,这可以将其销毁。setup
方法获取活动。get
破坏活动。destroy
的行为。...根据问题的示例:
onCleared
答案 2 :(得分:0)
我刚刚为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
}