kotlin – ArgumentCaptor vs InOrder用于验证具有不同参数的后续回调

我正在测试使用Architecture Components中的Room库生成的DAO类.我想检查加入多个表的查询返回的LiveData是否会在数据更改时更新.

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

使用ArgumentCaptor结果可以正常工作,这是这个问题的主题:

为什么ArgumentCaptor验证在这里工作,但InOrder没有?

看看how to verify multiple method calls with different 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)
    }
}

使用参数captor传递,但使用顺序失败并显示一条消息:

java.lang.AssertionError:
Expected :[]
Actual   :[Language(name=Java)]

最佳答案 TL; DR – 就其检查功能而言,这似乎是
Mockito-Kotlin’s部分的错误和/或错误文档.

Mockito-Kotlin的wiki says

If you want to do more assertions on the received argument, you can use check. […] If you want your test to fail inside a check invocation, you should make sure the body throws an error […]

检查calls Mockito’s argThat的实现,并将提供的谓词作为参数传递.但是,the documentation for ArgumentMatcher states

The method should never assert if the argument doesn’t match. It should only return false.

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

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

点赞