2016-01-13 3 views
19

У меня возникла проблема с захватом аргумента класса через ArgumentCaptor. Мой тестовый класс выглядит следующим образом:kotlin и ArgumentCaptor - IllegalStateException

@RunWith(RobolectricGradleTestRunner::class) 
@Config(sdk = intArrayOf(21), constants = BuildConfig::class) 
class MyViewModelTest { 
    @Mock 
    lateinit var activityHandlerMock: IActivityHandler; 

    @Captor 
    lateinit var classCaptor: ArgumentCaptor<Class<BaseActivity>> 

    @Captor 
    lateinit var booleanCaptor: ArgumentCaptor<Boolean> 

    private var objectUnderTest: MyViewModel? = null 

    @Before 
    fun setUp() { 
     initMocks(this) 
     ... 
     objectUnderTest = MyViewModel(...) 
    } 

    @Test 
    fun thatNavigatesToAddListScreenOnAddClicked(){ 
     //given 

     //when 
     objectUnderTest?.addNewList() 

     //then 
     verify(activityHandlerMock).navigateTo(classCaptor.capture(), booleanCaptor.capture()) 
     var clazz = classCaptor.value 
     assertNotNull(clazz); 
     assertFalse(booleanCaptor.value); 
    } 
} 

Когда я запускаю тест после исключения:
java.lang.IllegalStateException: classCaptor.capture() не должен быть пустым
Можно ли использовать аргумент похитителей в котлин?

========= ОБНОВЛЕНИЕ 1:
котлинский: 1.0.0-бета-4584
Mockito: 1.10.19
Robolectric: 3,0

====== === UPDATE 2:
StackTrace:

java.lang.IllegalStateException: classCaptor.capture() must not be null 

at com.example.view.model.ShoplistsViewModelTest.thatNavigatesToAddListScreenOnAddClicked(ShoplistsViewModelTest.kt:92) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at java.lang.reflect.Method.invoke(Method.java:606) 
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251) 
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188) 
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54) 
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152) 
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
at org.junit.runner.JUnitCore.run(JUnitCore.java:137) 
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) 
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) 
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at java.lang.reflect.Method.invoke(Method.java:606) 
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) 
+0

Просто попытался [ваш пример] (https://gist.github.com/miensol/d2a3c070f9efc50d8d2f), и он отлично работает. Каковы версии kotlin, mockito и roboelectric, которые вы используете? – miensol

+0

Спасибо за проверку. Я обновил вопрос. Какие версии вы попробовали? – Ramps

+0

Только что проверили [образец из gist] (https://gist.github.com/miensol/d2a3c070f9efc50d8d2f), и он отлично работает с версиями, которые вы указали. Кажется, что ваш код должен быть несколько иным. Можете ли вы опубликовать stacktrace? – miensol

ответ

13

возвращаемое значение classCaptor.capture() равно нулю, но подпись IActivityHandler#navigateTo(Class, Boolean) не допускает нулевой аргумент.

Библиотека mockito-kotlin предоставляет вспомогательные функции для решения этой проблемы.

+17

Просто добавив к вышеуказанному ответу для кого-либо еще, ища для этого: вам нужен com.nhaarman.mockito_kotlin.capture (classCaptor) вместо classCaptor.capture() – BhathiyaW

+1

И [здесь] (https://gist.github.com/spookyUnknownUser/f0ab1d3b9a99a87c0c0e0155abac81c9), как будет выглядеть тест из google codelabs с аргументом Captor – nmu

2

this solution Согласно моему решению здесь:

fun <T> uninitialized(): T = null as T 

//open verificator 
val verificator = verify(activityHandlerMock) 

//capture (would be same with all matchers) 
classCaptor.capture() 
booleanCaptor.capture() 

//hack 
verificator.navigateTo(uninitialized(), uninitialized()) 
12

Для некоторых, есть файл, MockitoKotlinHelpers.kt обеспечивается Google в Android репо архитектуры. Это обеспечивает удобный способ для вызова захвата .. просто позвоните verify(activityHandlerMock).navigateTo(capture(classCaptor), capture(booleanCaptor))

+0

Спасибо, это работает для меня. Часть GoogleSample об UnitTest хорошо стоит потратить! – angryd

+0

Я рад, что это работает для вас, сердитый. Конечно, я в основном ссылаюсь на их образец, когда я застреваю, и он также хорошо документирован. –

+0

Я думаю, что это должен быть принятый ответ. Использование fun capture (captor: ArgumentCaptor ) помогло мне без необходимости добавлять mockito-kotlin: – Kaskasi

0

Пришел сюда после того, как библиотека kotlin-Mockito не помогла. Я создал решение, используя отражение. Это функция, которая извлекает аргумент при условии к издевались-объект ранее:

fun <T: Any, S> getTheArgOfUsedFunctionInMockObject(mockedObject: Any, function: (T) -> S, clsOfArgument: Class<T>): T{ 
    val argCaptor= ArgumentCaptor.forClass(clsOfArgument) 
    val ver = verify(mockedObject) 
    argCaptor.capture() 
    ver.javaClass.methods.first { it.name == function.reflect()!!.name }.invoke(ver, uninitialized()) 
    return argCaptor.value 
} 
private fun <T> uninitialized(): T = null as T 

Применение: метод (Say я дразнил мой репозиторий и испытано ViewModel После вызова «обновление()» в ViewModel с. объект MenuObject, я хочу, чтобы убедиться, что на самом деле MenuObject призвал «updateMenuObject) (» метод репозитория:

viewModel.update(menuObjectToUpdate) 
val arg = getTheArgOfUsedFunctionInMockObject(mockedRepo, mockedRepo::updateMenuObject, MenuObject::class.java) 
assertEquals(menuObjectToUpdate, arg) 
Смежные вопросы