kio*_*oli 5 lambda android unit-testing mockito kotlin
我需要对一个函数进行单元测试,doClassAction并在它内部调用doStoreActionobject 上的函数store。这doStoreAction接受两个参数,一个整数和一个函数,如下例所示
class MyStore() {
fun doStoreAction(num: Int, callback: (text: String) -> Unit) {
// It does something
// ...
// and when it's done it calls the callback
return callback("some text")
}
}
class MyClass(private val store: MyStore,
private val objectX: MyObject) {
fun doClassAction(num: Int) {
store.doStoreAction(num) { callbackResult ->
// Action (potentially asynchronous)
// that will happen after obtaining the value via the callback
objectX.doObjectAction(callbackResult)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在使用Mockito,到目前为止我的测试有以下几行
@Test
fun doClassActionTest() {
val mockStore = mock(MyStore::class.java)
val mockObject = mock(MyObject::class.java)
val class = MyClass(mockStore, mockObject)
class.doClassAction(42)
}
Run Code Online (Sandbox Code Playgroud)
但它不会编译,因为对方法的调用没有指定回调......我不想覆盖它,因为我希望测试使用原始的并验证里面的东西被正确调用,例如:
when(objectX.doObjectAction(anyString())).doNothing()
verify(objectX, times(1)).doObjectAction(callbackResult)
Run Code Online (Sandbox Code Playgroud)
有没有办法调用该方法并模拟我在测试中设置的值的回调响应,但仍然让它通过原始代码流?
经过几天的尝试和阅读,我终于找到了一个有效的解决方案,我将在这里分享给任何发现自己处于同一个泡菜中的人
@Test
fun doClassActionTest() {
val mockStore = mock(MyStore::class.java)
val mockObject = mock(MyObject::class.java)
val myClass = MyClass(mockStore, mockObject)
val input = 42
val resultCallback = "test result"
`when`(mockStore.doStoreAction(any(), any())).then { invocation ->
(invocation.arguments[1] as (String) -> Unit).invoke(resultCallback)
}
myClass.doClassAction(input)
verify(mockObject, only()).doObjectAction(resultCallback)
}
Run Code Online (Sandbox Code Playgroud)
这里的警告是记住把你正在嘲笑的类和这些类调用的方法open放在 Kotlin 中,否则可能会出现误报
Ps 我还使用了一些从mockito-kotlin库中提取的内联函数,例如:
inline fun <reified T : Any> any() = Mockito.any(T::class.java) ?: createInstance<T>()
inline fun <reified T : Any> createInstance(): T = createInstance(T::class)
fun <T : Any> createInstance(kClass: KClass<T>): T = castNull()
@Suppress("UNCHECKED_CAST")
private fun <T> castNull(): T = null as T
Run Code Online (Sandbox Code Playgroud)