如何使用 mockk 对象和 junit.jupiter 测试挂起函数?
How do I test a suspend function using mockk objects and junit.jupiter?
我正在使用 Ktor 构建一个 Kotlin Slack Event API 应用程序,但我在测试我的功能时遇到了一些问题。
我有一个名为 SlackApi
的 class,除其他外,它将使用 KTor 客户端从 Slack API.
请求用户列表
import com.google.gson.Gson
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.url
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.readText
import io.ktor.http.HttpHeaders
data class SlackUserProfile(val real_name: String, val display_name: String, val real_name_normalized:String, val display_name_normalized: String)
data class SlackUser(val id: String, val profile: SlackUserProfile)
data class UserListResponse(val ok: Boolean, val members: List<SlackUser>)
class SlackApi(
private val client: HttpClient = HttpClient(CIO),
private val gson: Gson = Gson(),
private val bot_user_oauth_token: String = "bot-oauth-token-goes-here",
) {
suspend fun getUsers(): UserListResponse {
var builder = HttpRequestBuilder()
builder.url("https://slack.com/api/users.list")
builder.header(HttpHeaders.Authorization, "Bearer ${bot_user_oauth_token}")
val response = client.get<HttpResponse>(builder)
return gson.fromJson(response.readText(), UserListResponse::class.java)
}
}
我有一个(无效的)测试尝试:
import com.google.gson.Gson
import io.ktor.client.HttpClient
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.readText
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runBlockingTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class SlackApiTest {
private val client:HttpClient = mockk()
private val botToken: String = "123456-bottoken"
private val testObj = SlackApi(
client = client,
bot_user_oauth_token = botToken,
)
@Nested
inner class GetUsers {
@Test
fun `returns the list of users from the client`() = runBlockingTest {
val expectedUsers = listOf(SlackUser("blarg", SlackUserProfile("name", "name", "name", "name")))
val jsonUsers:String = Gson().toJson(expectedUsers)
var httpResponse:HttpResponse = mockk()
coEvery { httpResponse.readText() } returns jsonUsers
val builder = slot<HttpRequestBuilder>()
coEvery { client.get<HttpResponse>(capture(builder))
} coAnswers {
assertEquals("https://slack.com/api/users.list", builder.captured.url)
assertEquals("Bearer ${botToken}", builder.captured.headers.get(HttpHeaders.Authorization))
httpResponse
}
val allUsers = testObj.getUsers()
assertEquals(expectedUsers, allUsers.members)
}
}
}
但这行不通,这可能是因为我对这种培训的运作方式存在一些误解。相反,我收到堆栈跟踪错误:
java.io.EOFException: Premature end of stream: expected 1 bytes
at io.ktor.utils.io.core.StringsKt.prematureEndOfStream(Strings.kt:492)
at io.ktor.utils.io.core.internal.UnsafeKt.prepareReadHeadFallback(Unsafe.kt:78)
at io.ktor.utils.io.core.internal.UnsafeKt.prepareReadFirstHead(Unsafe.kt:61)
at io.ktor.utils.io.charsets.CharsetJVMKt.decode(CharsetJVM.kt:556)
at io.ktor.utils.io.charsets.EncodingKt.decode(Encoding.kt:103)
at io.ktor.utils.io.charsets.EncodingKt.decode$default(Encoding.kt:101)
at io.ktor.client.statement.HttpStatementKt.readText(HttpStatement.kt:173)
at io.ktor.client.statement.HttpStatementKt.readText$default(HttpStatement.kt:168)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invokeSuspend(SlackApiTest.kt:33)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invoke(SlackApiTest.kt)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invokeSuspend(RecordedBlockEvaluator.kt:26)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invoke(RecordedBlockEvaluator.kt)
at io.mockk.InternalPlatformDsl$runCoroutine.invokeSuspend(InternalPlatformDsl.kt:20)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:86)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at io.mockk.InternalPlatformDsl.runCoroutine(InternalPlatformDsl.kt:19)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invoke(RecordedBlockEvaluator.kt:26)
at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow.invoke(RecordedBlockEvaluator.kt:74)
at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:36)
at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
at io.mockk.MockKDsl.internalCoEvery(API.kt:98)
at io.mockk.MockKKt.coEvery(MockK.kt:116)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invokeSuspend(SlackApiTest.kt:33)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invoke(SlackApiTest.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:305)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.startCoroutineImpl(Builders.common.kt:192)
at kotlinx.coroutines.BuildersKt.startCoroutineImpl(Unknown Source)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:145)
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:84)
at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at my.slackbot.SlackApiTest$GetUsers.returns the list of users from the client(SlackApiTest.kt:28)
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.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod[=13=](ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke[=13=](ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:212)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
我需要能够测试此函数的行为,但我找不到任何详细说明我正在尝试做什么或是否可能的文档。
您可以使用 Ktor 的 MockEngine 来测试响应转换和请求 headers:
@Test
fun `returns the list of users from the client`(): Unit = runBlocking {
val expectedUsers = listOf(SlackUser("blarg", SlackUserProfile("name", "name", "name", "name")))
val expectedResponse = UserListResponse(true, expectedUsers)
val jsonResponse: String = Gson().toJson(expectedResponse)
val client = HttpClient(MockEngine) {
engine {
addHandler { request ->
assertEquals(request.url.toString(), "https://slack.com/api/users.list")
assertEquals(request.headers["Authorization"], "Bearer 123456-bottoken")
respond(content = jsonResponse)
}
}
}
val testObj = SlackApi(
client = client,
bot_user_oauth_token = "123456-bottoken",
)
assertEquals(expectedUsers, testObj.getUsers().members)
}
不幸的是,由于您描述的奇怪 Premature end of stream: expected 1 bytes
错误以及 top-level 内联方法的存在,我没有找到使用 mockk 库解决您的问题的方法HttpClient
class.
异常发生是因为函数 httpResponse.readText()
没有在这一行被模拟:
coEvery { httpResponse.readText() } returns jsonUsers
readText()
是 HttpResponse
上的扩展函数,必须使用 mockkStatic
函数对其进行模拟,例如:
@BeforeEach
fun setup() {
mockkStatic(HttpResponse::readText)
}
setup()
会在每个@Test
之前执行,因为它被标注了@BeforeEach
注解
我正在使用 Ktor 构建一个 Kotlin Slack Event API 应用程序,但我在测试我的功能时遇到了一些问题。
我有一个名为 SlackApi
的 class,除其他外,它将使用 KTor 客户端从 Slack API.
import com.google.gson.Gson
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.url
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.readText
import io.ktor.http.HttpHeaders
data class SlackUserProfile(val real_name: String, val display_name: String, val real_name_normalized:String, val display_name_normalized: String)
data class SlackUser(val id: String, val profile: SlackUserProfile)
data class UserListResponse(val ok: Boolean, val members: List<SlackUser>)
class SlackApi(
private val client: HttpClient = HttpClient(CIO),
private val gson: Gson = Gson(),
private val bot_user_oauth_token: String = "bot-oauth-token-goes-here",
) {
suspend fun getUsers(): UserListResponse {
var builder = HttpRequestBuilder()
builder.url("https://slack.com/api/users.list")
builder.header(HttpHeaders.Authorization, "Bearer ${bot_user_oauth_token}")
val response = client.get<HttpResponse>(builder)
return gson.fromJson(response.readText(), UserListResponse::class.java)
}
}
我有一个(无效的)测试尝试:
import com.google.gson.Gson
import io.ktor.client.HttpClient
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.readText
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runBlockingTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class SlackApiTest {
private val client:HttpClient = mockk()
private val botToken: String = "123456-bottoken"
private val testObj = SlackApi(
client = client,
bot_user_oauth_token = botToken,
)
@Nested
inner class GetUsers {
@Test
fun `returns the list of users from the client`() = runBlockingTest {
val expectedUsers = listOf(SlackUser("blarg", SlackUserProfile("name", "name", "name", "name")))
val jsonUsers:String = Gson().toJson(expectedUsers)
var httpResponse:HttpResponse = mockk()
coEvery { httpResponse.readText() } returns jsonUsers
val builder = slot<HttpRequestBuilder>()
coEvery { client.get<HttpResponse>(capture(builder))
} coAnswers {
assertEquals("https://slack.com/api/users.list", builder.captured.url)
assertEquals("Bearer ${botToken}", builder.captured.headers.get(HttpHeaders.Authorization))
httpResponse
}
val allUsers = testObj.getUsers()
assertEquals(expectedUsers, allUsers.members)
}
}
}
但这行不通,这可能是因为我对这种培训的运作方式存在一些误解。相反,我收到堆栈跟踪错误:
java.io.EOFException: Premature end of stream: expected 1 bytes
at io.ktor.utils.io.core.StringsKt.prematureEndOfStream(Strings.kt:492)
at io.ktor.utils.io.core.internal.UnsafeKt.prepareReadHeadFallback(Unsafe.kt:78)
at io.ktor.utils.io.core.internal.UnsafeKt.prepareReadFirstHead(Unsafe.kt:61)
at io.ktor.utils.io.charsets.CharsetJVMKt.decode(CharsetJVM.kt:556)
at io.ktor.utils.io.charsets.EncodingKt.decode(Encoding.kt:103)
at io.ktor.utils.io.charsets.EncodingKt.decode$default(Encoding.kt:101)
at io.ktor.client.statement.HttpStatementKt.readText(HttpStatement.kt:173)
at io.ktor.client.statement.HttpStatementKt.readText$default(HttpStatement.kt:168)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invokeSuspend(SlackApiTest.kt:33)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invoke(SlackApiTest.kt)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invokeSuspend(RecordedBlockEvaluator.kt:26)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invoke(RecordedBlockEvaluator.kt)
at io.mockk.InternalPlatformDsl$runCoroutine.invokeSuspend(InternalPlatformDsl.kt:20)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:86)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at io.mockk.InternalPlatformDsl.runCoroutine(InternalPlatformDsl.kt:19)
at io.mockk.impl.eval.RecordedBlockEvaluator$record$block.invoke(RecordedBlockEvaluator.kt:26)
at io.mockk.impl.eval.RecordedBlockEvaluator$enhanceWithRethrow.invoke(RecordedBlockEvaluator.kt:74)
at io.mockk.impl.recording.JvmAutoHinter.autoHint(JvmAutoHinter.kt:23)
at io.mockk.impl.eval.RecordedBlockEvaluator.record(RecordedBlockEvaluator.kt:36)
at io.mockk.impl.eval.EveryBlockEvaluator.every(EveryBlockEvaluator.kt:30)
at io.mockk.MockKDsl.internalCoEvery(API.kt:98)
at io.mockk.MockKKt.coEvery(MockK.kt:116)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invokeSuspend(SlackApiTest.kt:33)
at my.slackbot.SlackApiTest$GetUsers$returns the list of users from the client.invoke(SlackApiTest.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:305)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.startCoroutineImpl(Builders.common.kt:192)
at kotlinx.coroutines.BuildersKt.startCoroutineImpl(Unknown Source)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:145)
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:84)
at kotlinx.coroutines.BuildersKt.async$default(Unknown Source)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:49)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at my.slackbot.SlackApiTest$GetUsers.returns the list of users from the client(SlackApiTest.kt:28)
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.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod[=13=](ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke[=13=](ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod(TestMethodTestDescriptor.java:212)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
我需要能够测试此函数的行为,但我找不到任何详细说明我正在尝试做什么或是否可能的文档。
您可以使用 Ktor 的 MockEngine 来测试响应转换和请求 headers:
@Test
fun `returns the list of users from the client`(): Unit = runBlocking {
val expectedUsers = listOf(SlackUser("blarg", SlackUserProfile("name", "name", "name", "name")))
val expectedResponse = UserListResponse(true, expectedUsers)
val jsonResponse: String = Gson().toJson(expectedResponse)
val client = HttpClient(MockEngine) {
engine {
addHandler { request ->
assertEquals(request.url.toString(), "https://slack.com/api/users.list")
assertEquals(request.headers["Authorization"], "Bearer 123456-bottoken")
respond(content = jsonResponse)
}
}
}
val testObj = SlackApi(
client = client,
bot_user_oauth_token = "123456-bottoken",
)
assertEquals(expectedUsers, testObj.getUsers().members)
}
不幸的是,由于您描述的奇怪 Premature end of stream: expected 1 bytes
错误以及 top-level 内联方法的存在,我没有找到使用 mockk 库解决您的问题的方法HttpClient
class.
异常发生是因为函数 httpResponse.readText()
没有在这一行被模拟:
coEvery { httpResponse.readText() } returns jsonUsers
readText()
是 HttpResponse
上的扩展函数,必须使用 mockkStatic
函数对其进行模拟,例如:
@BeforeEach
fun setup() {
mockkStatic(HttpResponse::readText)
}
setup()
会在每个@Test
之前执行,因为它被标注了@BeforeEach
注解