kotlin和ArgumentCaptor - IllegalStateException

Ram*_*mps 37 android robolectric kotlin

我通过ArgumentCaptor捕获Class参数时遇到问题.我的测试类看起来像这样:

@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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我运行测试,下面抛出异常:
java.lang.IllegalStateException:classCaptor.capture()不能为null
是否可以使用参数的俘虏在科特林?

=========更新1:
Kotlin:1.0.0-beta-4584
Mockito:1.10.19
Robolectric:3.0

=========更新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)
Run Code Online (Sandbox Code Playgroud)

Mik*_*hot 32

返回值为classCaptor.capture()null,但签名IActivityHandler#navigateTo(Class, Boolean)不允许null参数.

的Mockito-科特林库提供支持功能来解决这个问题.

  • 只需为其他人搜索上述答案:您需要的是com.nhaarman.mockito_kotlin.capture(classCaptor)而不是classCaptor.capture() (35认同)
  • 并且[这里](https://gist.github.com/spookyUnknownUser/f0ab1d3b9a99a87c0c0e0155abac81c9)是google codelabs的测试看起来与参数Captor的关系 (3认同)
  • 我尝试了很多次来申请com.nhaarman.mockito_kotlin.capture,但没有成功。请参阅/sf/ask/2710099171/。有一个Wiki链接:https://github.com/nhaarman/mockito-kotlin/wiki/Mocking-and-verifying#argument-captors。因此,我尝试从导入中删除几个Mockito *库,将`verify`更改为com.nhaarman.mockito_kotlin.verify,问题消失了。 (3认同)
  • 我必须明确指定 T 类型不应为 null : `com.nhaarman.mockitokotlin2.capture&lt;MyType&gt;(argumentCaptor)` (3认同)

Ike*_*alu 29

对于某些人来说,谷歌在Android架构回购中提供了一个文件MockitoKotlinHelpers.kt.它提供了一种方便的方式来调用捕获..只需调用 verify(activityHandlerMock).navigateTo(capture(classCaptor), capture(booleanCaptor))

  • @IkechukwuKalu,我遇到了与使用注释“@Captor private Lateinit var SnackbarManagerArgumentCaptor: ArgumentCaptor&lt;ShowSnackbarEvent&gt;”和帮助器“verify(snackbarManager).show(capture(snackbarManagerArgumentCaptor))”解决的相同问题。 (6认同)
  • 添加后,我仍然遇到相同的问题:`fun &lt;T&gt; capture(argumentCaptor:ArgumentCaptor &lt;T&gt;):T = argumentsCaptor.capture()`然后在测试中:`val userCacheCaptor = ArgumentCaptor.forClass(User :: class .java)Mockito.verify(userCache,Mockito.times(1))。set(Mockito.anyString(),capture(userCacheCaptor))`错误是:`java.lang.IllegalStateException:capture(userCacheCaptor)不能为null ` (2认同)

ene*_*glu 6

使用kotlin-mockito https://mvnrepository.com/artifact/com.nhaarman/mockito-kotlin/1.5.0作为依赖性和示例代码,如下所示:

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

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

        }
Run Code Online (Sandbox Code Playgroud)


Suf*_*ian 6

正如CoolMind 在评论中所述,您首先需要为Kotlin-Mockito添加 gradle 导入,然后转移所有导入以使用此库。您的导入现在看起来像:

import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.isNull
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
Run Code Online (Sandbox Code Playgroud)

那么你的测试类将是这样的:

val mArgumentCaptor = argumentCaptor<SignUpInteractor.Callback>()

@Test
fun signUp_success() {
    val customer = Customer().apply {
        name = "Test Name"
        email = "test@example.com"
        phone = "0123444456789"
        phoneDdi = "+92"
        phoneNumber = ""
        countryCode = "92"
        password = "123456"
    }
    mPresenter.signUp(customer)
    verify(mView).showProgress()
    verify(mInteractor).createAccount(any(), isNull(), mArgumentCaptor.capture())
}
Run Code Online (Sandbox Code Playgroud)


new*_*rld 5

根据这个解决方案,我的解决方案在这里:

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())
Run Code Online (Sandbox Code Playgroud)