2016-01-13 10 views
19

ArgumentCaptorでClass引数を取り込む際に問題があります。私のテストクラスは次のようになります。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は()
は、それが可能な引数を使用することですnullであってはなりませんkotlinのキャプチャ?

========= UPDATE 1:
Kotlin:1.0.0-β-4584
Mockito:1.10.19
Robolectric:3.0

====== === UPDATE 2:
スタックトレース:

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を試してみました:MenuObjectオブジェクトは、私が実際にリポジトリの「updateMenuObject()」メソッドを呼びMenuObjectがあることを確認しますあなたが使っているkotlin、mockito、roboelectricのバージョンは何ですか? – miensol

+0

確認してくれてありがとう。私は質問を更新しました。あなたはどのバージョンを試しましたか? – Ramps

+0

[gistのサンプル](https://gist.github.com/miensol/d2a3c070f9efc50d8d2f)を再チェックしてください。リストされたバージョンで正常に動作します。あなたのコードは多少異なるはずです。あなたはスタックトレースを投稿できますか? – miensol

答えて

13

classCaptor.capture()の戻り値がnullであるが、の署名はヌル引数を許可しません。

mockito-kotlinライブラリは、この問題を解決するためのサポート機能を提供します。

+17

あなたが必要とするのは、classCaptor.capture()の代わりにcom.nhaarman.mockito_kotlin.capture(classCaptor)が必要です。 – 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

を、Androidのアーキテクチャのレポは、Googleが提供するファイル、MockitoKotlinHelpers.ktがあります。それはキャプチャを呼び出す便利な方法を提供します。ちょうど呼び出す verify(activityHandlerMock).navigateTo(capture(classCaptor), capture(booleanCaptor))

+0

ありがとうございます、それは私のために働いています。 UnitTestについてのGoogleSampleの部分は十分に価値のあるものです! – angryd

+0

私は怒って、あなたのためにうれしいです。確かに!、私は基本的に私が立ち往生したときはいつも彼らのサンプルを参照し、それもよく文書化されています。 –

+0

私はこれが受け入れられた答えであるべきだと思います。楽しいキャプチャ(キャプチャ: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 

使用上:。 (セイ、私は私のリポジトリを嘲笑し、ViewModelにをテストしているとのViewModelの「更新()」メソッドを呼び出した後

viewModel.update(menuObjectToUpdate) 
val arg = getTheArgOfUsedFunctionInMockObject(mockedRepo, mockedRepo::updateMenuObject, MenuObject::class.java) 
assertEquals(menuObjectToUpdate, arg) 
関連する問題