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)
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.
  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))   
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())
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)
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)
        }