我一直想为下面的API服务建立测试,但我不知道如何为它建立一个mock接口。
package com.example.themovieapp.network
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.Deferred
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
private const val BASE_URL = "https://api.themoviedb.org/3/"
private const val API_key = ""
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface MovieApiService{
//https://developers.themoviedb.org/3/movies/get-top-rated-movies
//https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Query.html
@GET("movie/top_rated")
fun getMoviesAsync(
@Query("api_key") apiKey: String = API_key,
@Query("language") language: String = "en-US",
@Query("page") page: Int
): Deferred<ResponseObject>
}
/*
Because this call is expensive, and the app only needs
one Retrofit service instance, you expose the service to the rest of the app using
a public object called MovieApi, and lazily initialize the Retrofit service there
*/
object MovieApi {
val retrofitService: MovieApiService by lazy {
retrofit.create(MovieApiService::class.java)
}
}
我想创建一个单元测试
验证HTTPS状态&
验证JSON响应是否合适。
如果能帮上忙的话,这是在另一个文件中用来创建API请求的。
coroutineScope.launch {
val getMoviesDeferred = MovieApi.retrofitService.getMoviesAsync(page = pageNumber)
//...
val responseObject = getMoviesDeferred.await()
//...
}
data class ResponseObject(
val page: Int,
val results: List<Movie>,
val total_results: Int,
val total_pages: Int
)
你可以通过 MockWebServer
class MovieApiTest {
private var mockWebServer = MockWebServer()
private lateinit var apiService: MovieApiService
@Before
fun setUp() {
// checkthis blogpost for more details about mock server
// https://medium.com/@hanru.yeh/unit-test-retrofit-and-mockwebserver-a3e4e81fd2a2
mockWebServer.start()
apiService = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(mockWebServer.url("/")) // note the URL is different from production one
.build()
.create(MovieApiService::class.java)
}
@After
fun teardown() {
mockWebServer.shutdown()
}
@Test
fun testCompleteIntegration() = runBlocking { // that will allow to wait for coroutine
mockWebServer.enqueue(MockResponse()
.setResponseCode(HttpURLConnection.HTTP_OK)
.setBody("""{
"page":0,
"total_results":1,
"total_pages":1,
"results": [{"id": "movie_id"}]
}"""))
val response = apiService.getMoviesAsync(page = 1).await()
assertEquals(0, response.page)
assertEquals(1, response.total_results)
assertEquals(1, response.total_pages)
assertEquals("movie_id", response.results.first().id)
}
}
这样你就可以避免调用真正的服务器,因为延迟和非确定的网络状态,在单元测试中不会有很好的效果。
另外,我也建议把代码拆分开来,这样如果你有复杂的解析逻辑,就可以独立测试它们:把字段做成可选的,并定义单独的映射器,可以检查JSON中哪些部分是必须的,哪些是必须的,并单独测试这个映射器。