java.lang.VerifyError Precise Reference expected while trying to runBlocking Kotlin suspended fun in Android

huangapple go评论73阅读模式
英文:

java.lang.VerifyError Precise Reference expected while trying to runBlocking Kotlin suspended fun in Android

问题

我正在尝试编写一个单元测试等待 Kotlin 暂停函数完成后再检查结果如下所示

@Test
fun shouldSetupThingsProperly() {
    val context = InstrumentationRegistry.getInstrumentation().context
    runBlocking { MyObject.enable(context, false) }
    Assert.assertTrue(/* 无论有用的 */ true)
}

暂停方法如下

object MyObject {

    @JvmStatic
    suspend fun enable(context: Context, enable: Boolean) {
        withContext(Dispatchers.IO) {
            // ... 做一些工作
            wakeup(context)
        }
    }

    private suspend fun wakeup(context: Context) {
        withContext(Dispatchers.IO) {
            try {
                // 设置一些内容...
            } catch (ignore: Exception) {}
        }
    }

}

测试运行结束后出现

java.lang.VerifyError: 验证器拒绝类 MyObject: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation) 无法验证: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation): [0x16] 寄存器 v7 具有类型 Reference: android.content.Context但期望类型为 Precise Reference: MyObject'MyObject' 的声明出现在 /data/app/test-_rphd0tDrOp0KM-Bz09NWA==/base.apk!classes2.dex 中
at MyObject.enable(Unknown Source:0)

我对协程不太熟悉想知道如何正确等待 enable 暂停函数在测试中完成或者错误是否由其他错误引起...
英文:

I'm trying to write a unit test waiting for completion of a kotlin suspended function before checking results like this :

@Test
fun shouldSetupThingsProperly() {
    val context = InstrumentationRegistry.getInstrumentation().context
    runBlocking { MyObject.enable(context, false) }
    Assert.assertTrue( /* whatever usefull */ true)
}

The suspending methods are as follow :

object MyObject {

    @JvmStatic
    suspend fun enable(context: Context, enable: Boolean) {
        withContext(Dispatchers.IO) {
            // ... do some work
            wakeup(context)
        }
    }

    private suspend fun wakeup(context: Context) {
        withContext(Dispatchers.IO) {
            try {
                // setup things ...
            } catch (ignore: Exception) {}
        }
    }

}

Test run ends with :

java.lang.VerifyError: Verifier rejected class MyObject: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation) failed to verify: java.lang.Object MyObject.enable(android.content.Context, boolean, kotlin.coroutines.Continuation): [0x16] register v7 has type Reference: android.content.Context but expected Precise Reference: MyObject (declaration of 'MyObject' appears in /data/app/test-_rphd0tDrOp0KM-Bz09NWA==/base.apk!classes2.dex)
at MyObject.enable(Unknown Source:0)

I'm not familiar with coroutine and I was wondering how to achieve waiting for completion of the enable suspended function inside the test properly or if error was due to some other mistake...

答案1

得分: 2

如果在Android或Flutter上使用coroutines的withContext出现问题,将coroutines库回退到1.3.6版本可以解决崩溃问题。
似乎在Android coroutines库版本1.3.7-1.3.8中存在VerifyError bug,将在1.4.0版本后修复。

详情请参见链接:
https://github.com/Kotlin/kotlinx.coroutines/issues/2049 https://github.com/Kotlin/kotlinx.coroutines/issues/2041

英文:

If it happend to coroutines- withContext on Android or Flutter, revert coroutines lib to 1.3.6 solved crash issue for me.
It seems that there is VerifyError bug in android coroutines lib version 1.3.7-1.3.8, and will be fixed after 1.4.0.

Details see links:

https://github.com/Kotlin/kotlinx.coroutines/issues/2049 https://github.com/Kotlin/kotlinx.coroutines/issues/2041

答案2

得分: 0

测试协程是一种技巧,即使有一些经验之后也是如此。
如果您可以导入,这将非常有帮助:https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test

如果您有了这个依赖,测试协程将变得更加可管理。

首先,如果您可以将运行此代码的调度程序作为变量或参数进行设置或覆盖,将有助于增加测试性。

至于编写测试,您可以进行以下操作:

```kotlin
@Before
fun before() {
    Dispatchers.setMain(mainThreadSurrogate)
}

@Test
fun shouldSetupThingsProperly() = runBlockingTest {
    val context = InstrumentationRegistry.getInstrumentation().context
    MyObject.enable(context, false, Dispatchers.Main)
    Assert.assertTrue( /* 无论有用的内容 */ true)
}

您的对象本身将会有更多的更改:

object MyObject {

    @JvmStatic
    suspend fun enable(context: Context, enable: Boolean, dispatcher: CoroutineDispatcher = Dispatchers.IO) {
         // 如果您需要返回值,可以随意使用带有上下文的 withContext,例如:
         // val result = withContext(dispatcher) { /* 返回值 */ 任何内容 }

        CoroutineScope(dispatcher).run {
            // ... 做一些工作
            wakeup(context)
        }
    }

    private suspend fun wakeup(context: Context) {
        // 在这里不需要另一个协程范围,它将自动继承父范围,因此您可以在这里调用异步函数
        delay(200)
        
        try {
            // 设置一些内容...
        } catch (exc: Exception) {
            // 发生了问题
        }
    }
}
英文:

Testing coroutines is a trick, even after some experience.
If you can import, this will be very helpful: https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test

If you have this dependency testing coroutines becomes much more manageable.

First off, if you can have the dispatcher you are running this a variable or parameter that can be set or overridden it will help you increase your testability.

As far as writing the test you can do something like:

@Before
fun before() {
    Dispatchers.setMain(mainThreadSurrogate)
}

@Test
fun shouldSetupThingsProperly() = runBlockingTest {
    val context = InstrumentationRegistry.getInstrumentation().context
    MyObject.enable(context, false, Dispatchers.Main)
    Assert.assertTrue( /* whatever useful */ true)
}

Your object itself will have I would say more of the changes

object MyObject {

    @JvmStatic
    suspend fun enable(context: Context, enable: Boolean, dispatcher: CoroutineDispatcher = Dispatchers.IO) {
         // If you need a return feel free to use withContext such as:
         // val result = withContext(dispatcher) { /* Return Value */ Any() }

        CoroutineScope(dispatcher).run {
            // ... do some work
            wakeup(context)
        }
    }

    private suspend fun wakeup(context: Context) {
        // Another coroutine scope is unnecessary here, it will inherit the parent scope automatically, so you can call
        // async functions here
        delay(200)
        
        try {
            // setup things ...
        } catch (exc: Exception) {
            // We had an issue
        }
    }
}

huangapple
  • 本文由 发表于 2020年4月8日 03:21:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/61087813.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定