Ktor 单元测试工具类

96 阅读1分钟

项目中目前使用Ktor来网络请求, 测试DataSource的时候需要构建一个HttpClient,下面提供了一个简单的封装类KtorMock

以下是简单用法:

internal class DataSource(private val httpClient: HttpClient) {
    suspend fun getAds(): List<Ad> {
       ...
    }
}

internal class DataSourceTest {
    private val ktorMock: KtorMock = KtorMock()
    private val dataSource: DataSource = DataSource(ktorMock.httpClient)
    
    // happy path
    @Test
    fun `given a valid json response then getAds returns ads data`() = runTest {
        val json = """
            {
              "code": "0",
              "message": "OK",
              "data": [{
                  "id":"1",
                  "name":"Test Ad",
                  "imageUrl":"https://example.com/image.jpg",
              }]
            }
         """.trimIndent()
         
         ktorMock.mockRespondOk(json)
         dataSource.getAds() shouldBe listOf(
            Ad(
              id="1", 
              name="Test Ad", 
              imageUrl="https://example.com/image.jpg",
            ),
         )
        
    }
    
    // bad path
    @Test
    fun `given bad request, then getAds returns empty list`() {
        ktorMock.mockRespondBadRequest()
        dataSource.getAds() shouldBe listOf()
    }
}
internal class KtorMock {

    private val respondProvider = mockk<(MockRequestHandleScope) -> HttpResponseData>()
    private val mockEngine = MockEngine { _ ->
        respondProvider(this)
    }

    val httpClient = HttpClient(mockEngine) {
        install(ContentNegotiation) {
            json(Json {
                ignoreUnknownKeys = true
                explicitNulls = false
            })
        }
        defaultRequest {
            header(HttpHeaders.ContentType, ContentType.Application.Json.toString())
        }
    }

    private fun mockResponse(block: MockRequestHandleScope.() -> HttpResponseData) {
        val slot = slot<MockRequestHandleScope>()
        every { respondProvider(capture(slot)) } answers {
            slot.captured.block()
        }
    }

    fun mockRespondOk(content: String) {
        mockResponse {
            respond(
                content = content,
                headers = headersOf(HttpHeaders.ContentType to listOf(ContentType.Application.Json.toString())),
            )
        }
    }

    fun mockRespondBadRequest() {
        mockResponse {
            respondBadRequest()
        }
    }
}