kotlin et ArgumentCaptor-IllegalStateException

j'ai un problème avec la capture de l'argument de classe via ArgumentCaptor. Mon cours d'essai ressemble à ceci:

@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);
    }
}

lorsque je fais le test, l'exception suivante est lancée:

Java.lang.IllegalStateException: classCaptor.capture () ne doit pas être null

Est-il possible d'utiliser des capteurs d'arguments dans kotlin?

========= Maj 1:

Kotlin: 1.0.0-bêta-4584

Mockito: 1.10.19

Roboléctrique: 3.0

========= Maj 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.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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access"151910920"0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:268)
at org.robolectric.RobolectricTestRunner.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)
23
demandé sur Ramps 2016-01-13 21:07:54

5 réponses

la valeur de retour de classCaptor.capture() est nulle, mais la signature de IActivityHandler#navigateTo(Class, Boolean) ne permet pas un argument nul.

la bibliothèque mockito-kotlin fournit des fonctions de soutien pour résoudre ce problème.

19
répondu Mike Buhot 2018-02-04 03:49:21

pour certains, il y a un fichier, MockitoKotlinHelpers.kt fourni par Google dans L'Architecture Android repo. Il fournit un moyen pratique pour la capture d'appel.. appelez simplement verify(activityHandlerMock).navigateTo(capture(classCaptor), capture(booleanCaptor))

21
répondu Ikechukwu Kalu 2018-04-12 15:12:31

selon cette solution ma solution ici:

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())
2
répondu neworld 2017-05-23 12:25:43

est venu ici après que la bibliothèque kotlin-Mockito n'ait pas aidé. J'ai créé une solution en réfléchissant. C'est une fonction qui extrait l'argument fourni à l'objet moqué plus tôt:

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

Usage: (Dire que je me suis moqué de mon dépôt et testé un modèle de vue. Après avoir appelé la méthode "update()" de viewModel avec un objet MenuObject, je veux m'assurer que MenuObject a effectivement appelé la méthode "updateMenuObject()" du dépôt:

viewModel.update(menuObjectToUpdate)
val arg = getTheArgOfUsedFunctionInMockObject(mockedRepo, mockedRepo::updateMenuObject, MenuObject::class.java)
assertEquals(menuObjectToUpdate, arg)
0
répondu Re'em 2018-01-21 05:23:02

Use kotlin-mockito https://mvnrepository.com/artifact/com.nhaarman/mockito-kotlin/1.5.0 comme code de dépendance et de l'échantillon comme écrit ci-dessous:

   argumentCaptor<Hotel>().apply {
            verify(hotelSaveService).save(capture())

            assertThat(allValues.size).isEqualTo(1)
            assertThat(firstValue.name).isEqualTo("Hilton Hotel")
            assertThat(firstValue.totalRoomCount).isEqualTo(10000L)
            assertThat(firstValue.freeRoomCount).isEqualTo(5000L)

        }
0
répondu enes.acikoglu 2018-06-17 10:54:37