使用 Android 协程模拟抛出异常

Mock throwing of Exception with Android Coroutines

我正在将协同程序引入我正在工作的 android 应用程序。有很多遗留问题,但我现在正在添加一些新的网络代码并为其使用协程。

编辑 更新以添加整个测试

coEvery { service.getAddress(any(), any(), any()) } coAnswers { throw Exception("Test Exception") }
val subject = repository.getAddress(123, "en", null)

coVerify(exactly = 1) { service.getAddress(any(), any(), any())}

所以测试中的 coEvery { service.getAddress(any(), any(), any()) } coAnswers { throw Exception("Test Exception") } 行本身不会造成问题。添加val subject = repository.getAddress(123, "en", null)

时下降

编辑 2 以包含 REPO 这是回购协议:

class AddressRepository (
    val addressRestService: AddressRestService,
    val dispatcher: CoroutineDispatcher
){
    suspend fun getAddress(
        wlid: Int,
        locale: String?,
        googleAddressObject: GeoResponse?
    ) : AddressForm = withContext(dispatcher) {
             addressRestService.getAddress(wlid, locale, googleAddressObject)
    }
}

我正在使用干净的架构,并且正在尝试测试我的存储库。我正在使用 Mockk 库,我正在尝试测试失败。

下面是一个片段:

coEvery {
    service.getAddress(123, "en", null)
} throw Exception()

当 运行 以上时,我通过以下堆栈跟踪获得测试失败:

java.lang.Exception
    at **.*******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invokeSuspend(AddressRepositoryTest.kt:52)
    at **.*******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invoke(AddressRepositoryTest.kt)
    at kotlinx.coroutines.test.TestBuildersKt$runBlockingTest$deferred.invokeSuspend(TestBuilders.kt:50)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch(TestCoroutineDispatcher.kt:50)
    at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:91)
    at kotlinx.coroutines.BuildersKt.async(Unknown Source)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:82)
    at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
    at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
    at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:73)
    at at **.*******.data.repository.AddressRepositoryTest$when getAddress is called, returns false(AddressRepositoryTest.kt:49)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy5.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:119)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:829)

我尝试了不同的变体,例如

coEvery {
    service.getAddress(123, "en", null)
}.coAnswers {
    throw Exception()
}

当 运行 以上时,我的测试失败,堆栈跟踪如下:

java.lang.Exception
    at **.****.******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invokeSuspend(AddressRepositoryTest.kt:53)
    at **.****.******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invoke(AddressRepositoryTest.kt)
    at io.mockk.MockKStubScope$coAnswers.invokeSuspend(API.kt:2112)
    at io.mockk.MockKStubScope$coAnswers.invoke(API.kt)
    at io.mockk.CoFunctionAnswer$answer.invokeSuspend(Answers.kt:33)
    at io.mockk.CoFunctionAnswer$answer.invoke(Answers.kt)
    at io.mockk.JvmCoroutineCall.callCoroutine(InternalPlatformDsl.kt:212)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at io.mockk.JvmCoroutineCall.callWithContinuation(InternalPlatformDsl.kt:217)
    at io.mockk.CoFunctionAnswer.answer(Answers.kt:34)
    at io.mockk.impl.stub.AnswerAnsweringOpportunity.answer(AnswerAnsweringOpportunity.kt:14)
    at io.mockk.impl.stub.MockKStub.answer(MockKStub.kt:54)
    at io.mockk.impl.recording.states.AnsweringState.call(AnsweringState.kt:16)
    at io.mockk.impl.recording.CommonCallRecorder.call(CommonCallRecorder.kt:53)
    at io.mockk.impl.stub.MockKStub.handleInvocation(MockKStub.kt:263)
    at io.mockk.impl.instantiation.JvmMockFactoryHelper$mockHandler.invocation(JvmMockFactoryHelper.kt:25)
    at io.mockk.proxy.jvm.advice.Interceptor.call(Interceptor.kt:20)
    at io.mockk.proxy.jvm.advice.BaseAdvice.handle(BaseAdvice.kt:42)
    at io.mockk.proxy.jvm.advice.jvm.JvmMockKProxyInterceptor.interceptNoSuper(JvmMockKProxyInterceptor.java:45)
    at **.****.******.data.api.AddressRestService$Subclass0.getAddress(Unknown Source)
    at **.****.******.data.repository.AddressRepository$getAddress.invokeSuspend(AddressRepository.kt:18)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch(TestCoroutineDispatcher.kt:50)
    at kotlinx.coroutines.test.internal.TestMainDispatcher.dispatch(MainTestDispatcher.kt:35)
    at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:170)
    at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
    at **.****.******.data.repository.AddressRepository.getAddress(AddressRepository.kt:17)
    at **.****.******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invokeSuspend(AddressRepositoryTest.kt:56)
    at **.****.******.data.repository.AddressRepositoryTest$when getAddress is called, returns false.invoke(AddressRepositoryTest.kt)
    at kotlinx.coroutines.test.TestBuildersKt$runBlockingTest$deferred.invokeSuspend(TestBuilders.kt:50)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.test.TestCoroutineDispatcher.dispatch(TestCoroutineDispatcher.kt:50)
    at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.async(Builders.common.kt:91)
    at kotlinx.coroutines.BuildersKt.async(Unknown Source)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.async$default(Builders.common.kt:82)
    at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
    at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
    at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:73)
    at **.****.******.data.repository.AddressRepositoryTest.when getAddress is called, returns false(AddressRepositoryTest.kt:49)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.rules.TestWatcher.evaluate(TestWatcher.java:61)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access0(ParentRunner.java:66)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
    at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
    at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
    at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
    at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
    at com.sun.proxy.$Proxy5.processTestClass(Unknown Source)
    at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:119)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
    at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
    at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
    at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:829)

如果有人能指出我哪里出错了,我将不胜感激.. 如果可以用协程模拟抛出异常。任何提示表示赞赏

您可以尝试使用JUnit Jupiter library, it allows to test throwing Exceptions by using assertThrows方法。例如,如果你想测试 service.getAddress() 函数在某些条件下抛出一些 Exception 你可以像下面这样测试它:

assertThrows<Exception> { service.getAddress(123, "en", null) }

此断言检查 getAddress 是否抛出 Exception 异常。您可以将 Exception 替换为您要测试的另一种类型。


如果你想抛出异常你可以使用throws中缀函数:

coEvery { service.getAddress(123, "en", null) } throws NullPointerException("Error occurred")

我猜你得到的错误是由于 return 值不正确造成的。请尝试 return AddressForm 对象而不是抛出 Exception:

coEvery { service.getAddress(any(), any(), any()) } returns AddressForm(...)