协程随机导致单元测试失败

时间:2019-03-07 15:49:38

标签: android unit-testing kotlin mockito coroutine

我的单元测试出现问题,原因是我认为是协程中的竞争条件,导致单元测试随机失败。

我有一个类,其中某些依赖项在我的UT中被模拟。 (我同时使用了Mockito和MockK)。单独运行测试时,它们永远不会失败,即使运行完整套件也不会导致测试失败。

但是,如果我进行了gradlew构建或完整的gradlew检查(ktlint + detekt + lint + UT),它们消耗的CPU较多,则其中一项测试会随机失败。

因此,对于某些背景:

我正在测试的班级有一个参与者,它接收两种类型的消息:入队和推入。方法register()接受一个参数并将其排队,直到经过设置的计时器,然后将其推入。如果再次调用register(),它将进入新参数,使用计时器取消协程并调用新参数。

suspend fun register(param: Param) {

    // This part suspends until the param is added to the DB
    val id = dao.addParam(param)

    actor.offer(Message.Enqueue(param))

    delayJob?.cancelAndJoin()
    delayJob = actor.sendDelayed(this, TIMER, Message.Push)
}

sendDelayed()是我创建的扩展

fun <E> SendChannel<E>.sendDelayed(
    scope: CoroutineScope,
    time: Long,
    element: E
) = scope.launch {
    delay(time)
    withContext(NonCancellable) {
        send(element)
    }
}

我正在测试的类的依赖项之一是CoroutineDispatcher,我在UT中将其设置为Dispatchers.Unconfined

例如,失败的测试之一是这样的:

@Mock
private lateinit var mockDao: Dao

private lateinit var classUnderTest: ClassUnderTest

@Before
fun setup() {
    MockitoAnnotations.initMocks(this)

    classUnderTest = ClassUnderTest(mockDao, Dispatchers.Unconfined)
}

@Test
fun `test that fail randomly`() {
    val param = Param()

    runBlocking {
        whenever(mockDao.addParam(param)) doReturn 1

        classUnderTest.register(param)
        delay(TIMER)
    }

    verifyBlocking(mockDao) { update(param) }
}

它以NullPointerException失败。不知何故模拟不被模拟。 我尝试使用MockK,结果相似,但无法说找不到答案(同样的原因,框架找不到模拟对象)。

在这一点上,我已经尝试消除延迟,更改调度程序等等。看来我不能依赖于为UT启动协程。我有一个类似的问题,一个简单的launch { val id = dao.get(param) }会导致UT随机失败,因为在检查时未设置id。

在我如何使用协同程序或如何对其进行测试方面,一定存在根本性的错误。非常感谢您的反馈。

谢谢

0 个答案:

没有答案