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

时间:2018-01-14 13:00:24

标签: kotlin mockito

我正在测试使用Room中的Architecture Components库生成的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)
    }
}

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

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

2 个答案:

答案 0 :(得分:1)

TL; DR - 就check功能而言,这似乎是Mockito-Kotlin's部分的错误和/或错误文档。

Mockito-Kotlin&#39; wiki says

  

如果要对收到的参数进行更多断言,可以使用check。 [...]如果您希望测试在check调用内失败,则应确保正文引发错误[...]

check calls Mockito's argThat的实现,并将提供的谓词作为参数传递。但是,the documentation for ArgumentMatcher states

  

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

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

我不确定如何解决此问题,但您现在可以完全避免使用check,并直接使用argThat(根据需要返回false,而不是投掷)。

答案 1 :(得分:0)

使用check(而不是Mockito-Kotlin提供的)功能似乎工作正常:

inline fun <reified T> check(noinline predicate: (T?) -> Unit): T? {
    return Mockito.argThat {
        try {
            predicate(it)
            true
        } catch (e: Throwable) {
            false
        }
    }
}

更新

此问题已在2.0.0-alpha04版本的mockito-kotlin修复。