Я тестировал свой класс DAO, сгенерированный с использованием библиотеки Room
из Architecture Components
. Я хотел проверить, будет ли LiveData
возвращаемая запросом, соединяющим несколько таблиц, при изменении данных.
Я начал с проверки InOrder
, но обнаружил, что любой аргумент, который я хотел бы утверждать, Mockito сказал бы, что метод был вызван с другим (и когда я изменил это утверждение на это, он сказал бы, что это был другой) ,
Использование ArgumentCaptor
оказалось подходящим для этой цели, что является предметом этого вопроса:
ArgumentCaptor
, но InOrder
этого не делает? Рассматривая ответы на вопрос о том, как проверять несколько вызовов методов с разными параметрами , оба метода должны работать нормально.
Вот упрощенная версия моего теста, которая демонстрирует проблему:
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)]
TL; DR – это, по-видимому, ошибка и / или плохая документация по части Mockito-Kotlin с точки зрения ее функции check
.
Вики Mockito-Kotlin говорит :
Если вы хотите сделать больше утверждений о полученном аргументе, вы можете использовать
check
. […] Если вы хотите, чтобы ваш тест вышел из строя внутри вызоваcheck
, вы должны убедиться, что тело выдает ошибку […]
Выполнение check
вызывает аргумент Mockito argThat
и передает предоставленный предикат в качестве аргумента. Однако в документации для ArgumentMatcher
указано :
Метод не должен утверждать, если аргумент не соответствует. Он должен возвращать только
false
.
Таким образом, документация Mockito-Kotlin находится в прямом противоречии с этим ограничением.
Я не уверен, как это исправить, но вы можете просто полностью исключить check
и использовать argThat
напрямую (вернув false
если argThat
, а не бросать).
Использование check
(вместо той, что предоставляется Mockito-Kotlin), функционирует подобно этому, кажется, работает нормально:
inline fun <reified T> check(noinline predicate: (T?) -> Unit): T? { return Mockito.argThat { try { predicate(it) true } catch (e: Throwable) { false } } }