Keyword: Ktor MockEngine, Unit Test
直到27日,完成KMM的測試功能放在
KMMDay27
有了基礎的Mock環境,接下來我們要來Mock 網路請求.Ktor官方也有提供假的Mock引擎以便我們使用,不用自己再手動開發一個.
同樣的,在gradle(shared)內我們進行Mock Engine的引用,我們是測試所以這次是放在commonTest內
//這是build.gradle.kts的soureSet內的Kotlin
val commonTest by getting {
dependencies {
...
implementation(Develop.Ktor.commonTest)
implementation(Develop.Ktor.cio)
...
}
}
而路徑如下
//這是在buildSrc內的Kotlin
object Ktor{
val commonTest = "io.ktor:ktor-client-mock:${Versions.ktor}"
val cio = "io.ktor:ktor-client-cio:${Versions.ktor}"
}
有了MockEnginge後,我們來建立一個使用MockEngine的MockApi
在commonTest下建立一個Mock package,然後裡面放入我們的CafeApi的Mock
//這是在commonTest下的Kotlin
class CafeApiMock(mockEngine:HttpClientEngine) : CafeApi {
companion object {
val tag = CafeApiMock::class.simpleName
}
private val client = HttpClient(mockEngine) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
override suspend fun fetchCafeFromApi(city: String): List<CafeResponseItem> {
return client.get("https://cafenomad.tw/api/v1.2/cafes/")
}
}
可以看到大部分都跟我們正常的CafeApiImpl相同,只是可以提供一個mockEngine來建立這個物件.
然後我們在commonTest下建立這個物件的測試流程,注意記得讓commonTest去繼承我們昨天寫好的BaseTest,這樣就能利用其中的方法建立一個給測試使用的coroutine,並且對雙平台的獨立測試來說沒有差別,KMM會幫我們自動選擇合適的實作.
//這是在commonTest底下的Kotlin語言
class CommonGreetingTest : BaseTest(){//繼承了BaseTest來使用Coroutine
@Test//標註Test說明這是一個Test Case
fun KtorTest() {
}
}
然後我們可以在KtorTest內建立我們的mockEngine,來控制我們的狀態與內容
val mockEngine = MockEngine { request ->//不管什麼request
respond(//都會回傳這樣
content = ByteReadChannel("""[{"id":"000703fe-cf8a-43c8-bd83-c90cfd61915f","name":"覺旅咖啡","address":"太陽系","city":"taipei"}]"""),
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
並且把這個mockEngine丟進去產生CafeApiMock
val apiMock = CafeApiMock(mockEngine)
最後建立回傳物件以作比較
val fakeResponseItem = CafeResponseItem("000703fe-cf8a-43c8-bd83-c90cfd61915f","太陽系","覺旅咖啡","taipei")
val responseItem = listOf(fakeResponseItem)
然後在Coroutine環境下進行測試
runTest{
assertEquals(apiMock.fetchCafeFromApi("taipei"),result)
}
整份的測試內容如下
@Test
fun KtorTest() {
val mockEngine = MockEngine { request ->
respond(
content = ByteReadChannel("""[{"id":"000703fe-cf8a-43c8-bd83-c90cfd61915f","name":"覺旅咖啡","address":"太陽系","city":"taipei"}]"""),
status = HttpStatusCode.OK,
headers = headersOf(HttpHeaders.ContentType, "application/json")
)
}
val apiMock = CafeApiMock(mockEngine)
val fakeResponseItem = CafeResponseItem("000703fe-cf8a-43c8-bd83-c90cfd61915f","太陽系","覺旅咖啡","taipei")
val result = listOf(fakeResponseItem)
runTest{
assertEquals(apiMock.fetchCafeFromApi("taipei"),result)
}
}
可以點旁邊的綠色三角形來跑跑看.
明天我們會來跑看看SQLDelight的測試內容