Kotlin 协程单元测试错误:线程“main @coroutine#1 @coroutine#2”中的异常 java.lang.NullPointerException

Posted

技术标签:

【中文标题】Kotlin 协程单元测试错误:线程“main @coroutine#1 @coroutine#2”中的异常 java.lang.NullPointerException【英文标题】:Kotlin Coroutine Unit Testing Error: Exception in thread "main @coroutine#1 @coroutine#2" java.lang.NullPointerException 【发布时间】:2018-10-15 11:41:40 【问题描述】:

我正在尝试对使用 Kotlin 协程制作的演示者进行单元测试,这也是我第一次使用 Mockito

每当我尝试运行单元测试时,它在协程中第一次尝试对我的视图执行任何操作时都会收到以下错误

Exception in thread "main @coroutine#1 @coroutine#2" java.lang.NullPointerException
    at .signin.SignInPresenter$subscribe$1.doResume(SignInPresenter.kt:45)
    at kotlin.coroutines.experimental.jvm.internal.CoroutineImpl.resume(CoroutineImpl.kt:54)
    at kotlinx.coroutines.experimental.DispatchedKt.resumeCancellable(Dispatched.kt:208)
    at kotlinx.coroutines.experimental.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:35)
    at kotlinx.coroutines.experimental.CoroutineStart.invoke(CoroutineStart.kt:111)
    at kotlinx.coroutines.experimental.AbstractCoroutine.start(AbstractCoroutine.kt:161)
    at kotlinx.coroutines.experimental.BuildersKt.launch(Builders.kt:68)
    at kotlinx.coroutines.experimental.BuildersKt.launch$default(Builders.kt:61)
    at .signin.SignInPresenter.subscribe(SignInPresenter.kt:42)

这是我的presenter的相关部分,错误中引用的行是view.showSigninPanel

class SignInPresenter(
    private val view: SignInContract.View,
    private val userRepo: ParseAuthController,
    private val contextPool: CoroutineContextProvider
) : SignInContract.Presenter 

    private val coroutineJobs: MutableList<Job> = mutableListOf()

    override fun subscribe() 
        view.loadBackgroundImage()
        view.setUpSignInPanel()
        view.setUpKeyboardListener()

        coroutineJobs.add(launch(contextPool.Main) 
            if (!userRepo.isAuthenticated()) 
                view.showSignInPanel()
                subscribeToLoginValidation()
                subscribeToPaswordEmailValidation()
             else 
                view.launchMainActivity()
            
        )
    

userRepo.isAuthenticated() 的调用是暂停调用

根据这篇文章,我将一个测试协程上下文传递给我的演示者

https://android.jlelse.eu/mastering-coroutines-android-unit-tests-8bc0d082bf15

class TestContextProvider : CoroutineContextProvider() 
    override val Main: CoroutineContext = Unconfined
    override val IO: CoroutineContext = Unconfined

这是我目前的单元测试

class SignInPresenterTest 

    private lateinit var view: SignInContract.View
    private lateinit var presenter: SignInPresenter
    private lateinit var parseAuthController: ParseAuthController

    @Before
    fun setUp() 
        view = mock(SignInContract.View::class.java)

        parseAuthController = mock(ParseAuthController::class.java)

        presenter = SignInPresenter(
            view,
            parseAuthController,
            TestContextProvider()
        )
    

    @After
    fun tearDown() 
        presenter.dispose()
    

    @Test
    fun subscribeNotAuthenticatedShowSignInPanel() 
        runBlocking 
            val expectedResult = false
            `when`(parseAuthController.isAuthenticated()).thenReturn(expectedResult)

            presenter.subscribe()
        

        verify(view).showSignInPanel()
    

更新:我一直在做一些额外的测试,看起来如果我在演示者if (!userRepo.isAuthenticated()) 中删除暂停调用,它不会崩溃,我可以验证 Mockito 是否运行了某些东西,但这不是不是一个实际的解决方案...但是该挂起呼叫的某些问题导致了问题?

【问题讨论】:

你能分享代码吗,我也有同样的问题,lib 已更新但不起作用 我无法再访问这个代码库了。也许尝试做一个 ./gradlew build --refresh-dependencies 看看是否能解决它? 【参考方案1】:

问题解决了!

上面的代码其实是正确的。我有错误的 Mockito 依赖项。

我从一个同事的 repo 中复制了一些依赖项,他们正在使用这个库,它提供了一些 Kotlin 特定的功能

https://github.com/nhaarman/mockito-kotlin

要么有一些东西破坏了对协同程序的支持,要么可能有一些不同的要求才能让它工作?

更新到最新的 Mockito 依赖项有效。

【讨论】:

我也有这个库,但我删除了 ti 仍然看到这个结果。

以上是关于Kotlin 协程单元测试错误:线程“main @coroutine#1 @coroutine#2”中的异常 java.lang.NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

kotlin 协程withContext切换线程

kotlin协程

Kotlin协程的Jacoco代码覆盖率不正确

kotlin协程

kotlin协程

kotlin协程