iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 27
0
自我挑戰組

Android Architecture 及 Unit Test系列 第 27

[Day 27] Test:Part 9 API Test

  • 分享至 

  • xImage
  •  

今天來寫一些 API 相關的測試,不過因為這個專案沒有用到網路相關的東西,後面的內容會以 github api 展示。

測試 API 的方式很多,而我自己比較喜歡的做法是利用 MockWebServer 協助我完成測試。

什麼是 MockWebServer

MockWebServer 是 Square 公司為 OkHttp 開發的 Mock Library,其原理是創建一個 Local 端的 web server, 使用時再把透過替換 domain 的方式讓這個假的 web server handle 住網路請求,並提供一些如 request 、 response 、 header 等相關的 API 讓使用者可以拿來做驗證。

Gradle

先把 Retrofit 及 MockWebServer 的 library 加進來:


dependency {
    def retrofitVersion = '2.6.1'
    def mockwebserverVersion = '3.8.1'

    implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
    implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
    implementation "com.squareup.okhttp3:mockwebserver:$mockwebserverVersion"
}

寫一個測試用的網路請求 API

寫一個 API 獲得 coil 這個 repository 的資料:

interface SearchService {
    @GET("search/repositories")
    suspend fun searchRepo(
        @Query("q", encoded = true) repo: String = "coil+org:coil-kt"
    ): SearchRepo
}

@Module
class NetworkModule {

    @Singleton
    @Provides
    fun provideGithubService(): SearchService {
        return Retrofit.Builder()
            .baseUrl("https://api.github.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(SearchService::class.java)
    }
}

順便一提 Retrofit 在 2.6.0 之後已經可以支援 coroutines 的 suspend function 了。

準備假資料

我們需要為 MockWebServer 提供假資料,讓他可以 Mock 假的 Http Response ,這裏做法隨意,我是在 test 資料夾下建立一個 Java 的 resource 資料夾,把 SearchService 的回應 Json 檔放進去,之後再用 Java 的 IO API 把 Json 檔的資料讀出來,傳給 MockWebServer

在專案內放入假的 API Json 資料:

寫一個 API Test

在 Test 資料夾建立測試,我們不會用到 Android 相關的 API ,並在 Before 階段初始化 MockWebServer :

@ExperimentalCoroutinesApi
class SearchServiceTest {

    private lateinit var service: SearchService

    private lateinit var mockWebServer: MockWebServer

    @Before
    fun setup() {
        mockWebServer = MockWebServer()
        service = Retrofit.Builder()
            .baseUrl(mockWebServer.url("/"))
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(SearchService::class.java)
    }

    @After
    fun dropdown() {
        mockWebServer.shutdown()
    }
}

接著把 Retrofit domain 替換成 MockWebServer ,值得注意的是 MockWebServer 要在 After 階段 shutdown 。

最後完成一個測試:

@ExperimentalCoroutinesApi
class SearchServiceTest {

    ......

    @Test
    fun searchRepoThenResponse() {
        val query = "coil+org:coil-kt"
        enqueueResponse("search-repo.json")
        val response = runBlocking {
            service.searchRepo(query)
        }
        val request = mockWebServer.takeRequest()

        assertThat(request.path, `is`("/search/repositories?q=$query"))
        val items = response.items
        assertThat(items.size, `is`(1))
        assertThat(items[0].name, `is`("coil"))
        assertThat(items[0].fullName, `is`("coil-kt/coil"))
        assertThat(items[0].gitUrl, `is`("git://github.com/coil-kt/coil.git"))
        assertThat(items[0].owner.login, `is`("coil-kt"))
    }

    private fun enqueueResponse(fileName: String, headers: Map<String, String> = emptyMap()) {
        val classloader = javaClass.classLoader
        val inputStream = classloader.getResourceAsStream("api-response/$fileName")
        val source = Okio.buffer(Okio.source(inputStream))
        val mockResponse = MockResponse()
        for ((key, value) in headers) {
            mockResponse.addHeader(key, value)
        }
        mockWebServer.enqueue(
            mockResponse
                .setBody(source.readString(Charsets.UTF_8))
        )
    }
}

我寫了一個 enqueueResponse() 方法來讀取 Json 檔的資料,並把它設置成 Response 的 body ,之後再調用 API 時就可以接收到 API 資料。

另外還可以使用他自己的 API 來測試 API path 是否與自己所設定的一致,這樣就完成一個 API 測試了。

API Test 的介紹到這邊,因為剩下的內容是把程式碼完善,明天開始會講一些其他的東西。


上一篇
[Day 26] Test:Part 8 Navigation Test
下一篇
[Day 28] 程式的可測試性
系列文
Android Architecture 及 Unit Test30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言