首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于MockWebServer的悬吊功能测试

基于MockWebServer的悬吊功能测试
EN

Stack Overflow用户
提问于 2020-06-02 21:29:38
回答 1查看 1.7K关注 0票数 7

我正在测试使用MockWebServer的挂起函数返回结果的api,但是除非使用launch构建器,否则它不适用于runBlockingTest、testCoroutineDispatcher、testCorounieScope,为什么?

代码语言:javascript
复制
abstract class AbstractPostApiTest {

    internal lateinit var mockWebServer: MockWebServer

    private val responseAsString by lazy {
        getResourceAsText(RESPONSE_JSON_PATH)
    }

    @BeforeEach
    open fun setUp() {
        mockWebServer = MockWebServer()
        println("AbstractPostApiTest setUp() $mockWebServer")
    }


    @AfterEach
    open fun tearDown() {
        mockWebServer.shutdown()
    }

    companion object {
        const val RESPONSE_JSON_PATH = "posts.json"
    }


    @Throws(IOException::class)
    fun enqueueResponse(
        code: Int = 200,
        headers: Map<String, String>? = null
    ): MockResponse {

        // Define mock response
        val mockResponse = MockResponse()
        // Set response code
        mockResponse.setResponseCode(code)

        // Set headers
        headers?.let {
            for ((key, value) in it) {
                mockResponse.addHeader(key, value)
            }
        }

        // Set body
        mockWebServer.enqueue(
            mockResponse.setBody(responseAsString)
        )

        return mockResponse
    }


}


class PostApiTest : AbstractPostApiTest() {

    private lateinit var postApi: PostApiCoroutines

    private val testCoroutineDispatcher = TestCoroutineDispatcher()

    private val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)

    @BeforeEach
    override fun setUp() {
        super.setUp()

        val okHttpClient = OkHttpClient
            .Builder()
            .build()

        postApi = Retrofit.Builder()
            .baseUrl(mockWebServer.url("/"))
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
            .create(PostApiCoroutines::class.java)


        Dispatchers.setMain(testCoroutineDispatcher)

    }

    @AfterEach
    override fun tearDown() {
        super.tearDown()

        Dispatchers.resetMain()
        try {
            testCoroutineScope.cleanupTestCoroutines()
        } catch (exception: Exception) {
            exception.printStackTrace()
        }
    }

    @Test
    fun `Given we have a valid request, should be done to correct url`() =
        testCoroutineScope.runBlockingTest {

            // GIVEN
            enqueueResponse(200, RESPONSE_JSON_PATH)

            // WHEN
              postApi.getPostsResponse()

            advanceUntilIdle()

            val request = mockWebServer.takeRequest()

            // THEN
            Truth.assertThat(request.path).isEqualTo("/posts")

        }
}

结果错误:java.lang.IllegalStateException: This job has not completed yet

如果使用launch构建器,则此测试无效,如果使用launch构建器,则不需要testCoroutineDispatchertestCoroutineScope,原因是什么?正常情况下,挂起函数传递而不处于另一个作用域,即使使用runBlockingTest

代码语言:javascript
复制
 @Test
    fun `Given we have a valid request, should be done to correct url`() =
        runBlockingTest {

            // GIVEN
            enqueueResponse(200, RESPONSE_JSON_PATH)

            // WHEN
            launch {
                postApi.getPosts()
            }

            val request = mockWebServer.takeRequest()

            // THEN
            Truth.assertThat(request.path).isEqualTo("/posts")

        }

上面那个起作用了。

此外,下面的测试也通过了一些时间。

@Test Given api return 200, should have list of posts() = testCoroutineScope.runBlockingTest {

代码语言:javascript
复制
    // GIVEN
    enqueueResponse(200)

    // WHEN
    var posts: List<Post> = emptyList()
    launch {
        posts = postApi.getPosts()
    }

    advanceUntilIdle()

    // THEN
    Truth.assertThat(posts).isNotNull()
    Truth.assertThat(posts.size).isEqualTo(100)

}

我尝试了许多不用posts = postApi.getPosts()调用launch的组合,使用async,将enqueueResponse(200)放入异步async { enqueueResponse(200) }.await()中,但是测试失败了,有时它通过了,有时不是每个组合中的一些。

EN

回答 1

Stack Overflow用户

发布于 2021-07-05 01:26:45

在完成运行测试的协同线之前,有一个runBlockTest不等待其他线程/作业完成的错误。

我成功地尝试了使用runBlocking (我使用了令人敬畏的Hamcrest到Kotlin 汉克莱斯特的端口)

代码语言:javascript
复制
fun `run test` = runBlocking {
  mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(""))

  // make HTTP call 

  val result = mockWebServer.takeRequest(2000L, TimeUnit.MILLISECONDS)

  assertThat(result != null, equalTo(true))
}

这里有几件事要注意:

  1. 使用线程阻塞调用时,永远不会在没有超时的情况下调用。总是最好什么都不做就失败,然后永远阻止线程。
  2. runBlocking的使用可能会被一些人认为不是。然而,这篇博客文章概述了运行并发代码的不同方法,以及它们的不同用例。我们通常希望使用runBlockingTest或(TestCoroutineDispatcher.runBlockingTest),以便我们的测试代码和应用程序代码是同步的。通过使用相同的调度程序,我们可以确保作业全部完成等。TestCoroutineDispatcher还具有方便的“时钟”功能,以使延迟消失。但是,在测试应用程序的HTTP层时,以及在单独的线程上运行模拟服务器时,我们有一个同步点为takeRequest。因此,我们可以很高兴地使用runBlocking,允许我们使用协同器和运行在不同线程上的模拟服务器一起工作,没有任何问题。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62161708

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档