ArgumentCaptor 与 InOrder 以验证具有不同参数的后续回调

are*_*lek 5 mockito kotlin

我测试使用生成的我DAO类Room从库Architecture Components。我想检查LiveData连接多个表的查询返回的数据是否会在数据更改时更新。

我从使用InOrder验证开始,但发现无论我想断言什么参数,Mockito 都会说该方法是用不同的方法调用的(当我将断言更改为那个时,它会说是另一个)。

使用ArgumentCaptor结果证明可以很好地用于此目的,这是这个问题的主题:

为什么ArgumentCaptor验证在这里起作用,但InOrder不起作用?

查看有关如何使用不同 params 验证多个方法调用的问题的答案,这两种方法都应该可以正常工作。

这是我的测试的简化版本,展示了这个问题:

package com.example

import com.nhaarman.mockito_kotlin.argumentCaptor
import com.nhaarman.mockito_kotlin.check
import com.nhaarman.mockito_kotlin.mock
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnitRunner

@Suppress("IllegalIdentifier")
@RunWith(MockitoJUnitRunner::class)
class MultipleCallbacksVanillaTest {

    private val java = Language("Java")
    private val javascript = Language("JavaScript")
    private val kotlin = Language("Kotlin")

    private val firstList = emptyList<Language>()
    private val secondList = listOf(java)
    private val thirdList = listOf(java, javascript, kotlin)
    private val lastList = listOf(java, kotlin)

    @Test fun `using argument captor`() {
        // given
        val observer = mock<Observer<List<Language>>>()
        val liveData = MutableLiveData<List<Language>>()

        // when
        liveData.observeForever(observer)
        liveData.value = firstList
        liveData.value = secondList
        liveData.value = thirdList
        liveData.value = lastList

        // then
        argumentCaptor<List<Language>>().run {
            verify(observer, times(4)).onChanged(capture())
            val (firstValue, secondValue, thirdValue, lastValue) = allValues
            assertEquals(firstList, firstValue)
            assertEquals(secondList, secondValue)
            assertEquals(thirdList, thirdValue)
            assertEquals(lastList, lastValue)
        }
    }

    @Test fun `using in order`() {
        // given
        val observer = mock<Observer<List<Language>>>()
        val liveData = MutableLiveData<List<Language>>()

        // when
        liveData.observeForever(observer)
        liveData.value = firstList
        liveData.value = secondList
        liveData.value = thirdList
        liveData.value = lastList

        // then
        inOrder(observer).run {
            verify(observer).onChanged(check { assertEquals(firstList, it) })
            verify(observer).onChanged(check { assertEquals(secondList, it) })
            verify(observer).onChanged(check { assertEquals(thirdList, it) })
            verify(observer).onChanged(check { assertEquals(lastList, it) })
        }
        verifyNoMoreInteractions(observer)
    }

}

data class Language(val name: String)

interface Observer<in T> {
    fun onChanged(value: T?)
}

class MutableLiveData<T : Any> {
    var value: T
        get() = _value
        set(value) {
            observers.forEach { it.onChanged(value) }
            _value = value
        }

    private lateinit var _value: T

    private var observers = mutableSetOf<Observer<T>>()

    fun observeForever(observer: Observer<T>) {
        if (::_value.isInitialized) observer.onChanged(_value)
        observers.add(observer)
    }
}
Run Code Online (Sandbox Code Playgroud)

using argument captor通过,但using in order失败并显示一条消息:

java.lang.AssertionError:
Expected :[]
Actual   :[Language(name=Java)]
Run Code Online (Sandbox Code Playgroud)

Oli*_*rth 1

TL;DR -就其功能而言,这似乎是Mockito-Kotlincheck方面的一个错误和/或糟糕的文档。

Mockito-Kotlin 的wiki 说道

如果您想对收到的参数进行更多断言,可以使用check. [...]如果您希望测试在调用内失败check,则应确保主体抛出错误[...]

的实现check 调用 Mockito 的argThat,并将提供的谓词作为参数传递。但是,文档说明ArgumentMatcher

如果参数不匹配,该方法永远不应该断言。它应该只返回false

因此,Mockito-Kotlin 的文档与此约束直接矛盾。

我不知道如何解决这个问题,但你check现在可以完全避免,并argThat直接使用(适当返回false,而不是抛出)。