Thr*_*ian 6 android unit-testing mockwebserver kotlin-coroutines
我正在测试使用 MockWebServer 的挂起函数返回结果的 api,但它不适用于 runBlockingTest、testCoroutineDispatcher、testCorounieScope,除非使用了launch构建器,为什么?
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")
}
}
Run Code Online (Sandbox Code Playgroud)
结果错误: java.lang.IllegalStateException: This job has not completed yet
如果使用launchbuilder 则此测试不起作用,如果使用launchbuilder 则不需要testCoroutineDispatcheror testCoroutineScope,这是什么原因?通常挂起函数通过而不在另一个范围内,即使有runBlockingTest
@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")
}
Run Code Online (Sandbox Code Playgroud)
上面那个有效。
下面的测试也通过了一些时间。
@Test fun Given api return 200, should have list of posts() = testCoroutineScope.runBlockingTest {
// 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)
}
Run Code Online (Sandbox Code Playgroud)
我尝试了许多组合调用posts = postApi.getPosts()而没有launch, using async, put enqueueResponse(200)inside async async { enqueueResponse(200) }.await()但测试失败了,有时它通过有时它没有一些与每个组合。
runBlockTest存在一个错误,即在完成正在运行的测试的协程之前不等待其他线程/作业完成。
我尝试使用runBlocking成功(我使用 Hamcrest 到 Kotlin Hamkrest的出色端口)
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))
}
Run Code Online (Sandbox Code Playgroud)
这里有几点需要注意:
决不应该在没有超时的情况下调用线程阻塞调用。总是最好什么都失败,然后永远阻塞线程。
有些人可能认为使用runBlocking是不行的。然而,这篇博文概述了运行并发代码的不同方法,以及它们的不同用例。我们通常希望使用runBlockingTestor ( TestCoroutineDispatcher.runBlockingTest) 以便我们的测试代码和应用程序代码同步。通过使用相同的调度程序,我们可以确保作业全部完成,等等,TestCoroutineDispatcher还有方便的“时钟”功能来消除延迟。然而,当测试应用程序的 HTTP 层时,如果有一个模拟服务器在单独的线程上运行,我们的同步点是takeRequest。因此我们可以愉快地使用runBlocking协程和在不同线程上运行的模拟服务器一起工作,没有任何问题。
| 归档时间: |
|
| 查看次数: |
775 次 |
| 最近记录: |