Тестирование OkHttp с MockWebServer и MockResponse с телом буфера - PullRequest
0 голосов
/ 16 мая 2018

Я пытаюсь протестировать некоторый код загрузки с использованием OkHttp3 и с треском провалился Цель: проверка загрузка файла изображения и проверка его работоспособности. Платформа: Android. Этот код работает в рабочей среде, но тестовый код не имеет никакого смысла.

Код продукта

class FileDownloaderImpl internal constructor(
    private val ioScheduler: Scheduler,
    private val logger: LoggingInterceptor,
    private val parser: ((String) -> HttpUrl)? // for testing only
) : FileDownloader {

    @Inject constructor(logger: LoggingInterceptor) : this(Schedulers.io(), logger, null)

    override fun downloadFile(url: String, destination: File): Single<File> {
        Logger.d(TAG, "downloadFile\nurl = $url\nfile = $destination")

        val client = OkHttpClient.Builder()
            .addInterceptor(logger)
            .build()

        val call = client.newCall(newRequest(url))
        return Single.fromCallable { call.execute() }
            .doOnDispose { call.cancel() }
            .subscribeOn(ioScheduler)
            .map { response ->
                Logger.d(TAG, "Successfully downloaded board: $response")
                return@map response.body()!!.use { body ->
                    Okio.buffer(Okio.sink(destination)).use { sink ->
                        sink.writeAll(body.source())
                    }
                    destination
                }
            }
    }

    /**
     * Creates the request, optionally parsing the URL into an [HttpUrl]. The primary (maybe only)
     * use-case for that is for wrapping the URL in a `MockWebServer`.
     */
    private fun newRequest(url: String): Request {
        val httpUrl = parser?.invoke(url)
        val builder = Request.Builder()
        httpUrl?.let { builder.url(it) } ?: builder.url(url)
        return builder.build()
    }
}

Тестовый код (JUnit5)

@ExtendWith(TempDirectory::class)
internal class FileDownloaderImplTest {

    private val mockWebServer = MockWebServer()
    private val logger = LoggingInterceptor(HttpLoggingInterceptor.Level.BODY) { msg -> println(msg) }
    private val fileDownloader = FileDownloaderImpl(Schedulers.trampoline(), logger) {
        mockWebServer.url("/$it")
    }

    @BeforeEach fun setup() {
        mockWebServer.start()
    }

    @AfterEach fun teardown() {
        mockWebServer.shutdown()
    }

    @Test fun downloadFile(@TempDir tempDirectory: Path) {
        // Given
        val res = javaClass.classLoader.getResource("green20.webp")
        val f = File(res.path)
        val buffer = Okio.buffer(Okio.source(f)).buffer()
        mockWebServer.enqueue(MockResponse().setBody(buffer))
        val destFile = tempDirectory.resolve("temp.webp").toFile()

        // Verify initial condition
        destFile.exists() shouldBe false

        // When
        fileDownloader.downloadFile("test.html", destFile)

            // Then
            .test()
            .assertValue { file ->
                file.exists() shouldBe true
                file.length() shouldEqualTo 66 // FAIL: always 0
                true
            }
    }
}

Подробнее

"green20.webp" - это файл, который существует в app/test/resources. Когда я отлаживаю, все признаки того, что он существует. Что касается отладки, у меня есть точки останова в коде prod, и похоже, что у объекта Response (предположительно MockResponse) нет тела. Я понятия не имею, почему это будет.

Актуальные идеи:

  1. Я не добавляю корректное тело ответа
  2. Файл как-то "открыт", поэтому его длина всегда равна 0, хотя на самом деле он не пустой.

EDIT

Я попытался удалить MockWebServer из теста и инициировал real загрузку, и мой тест фактически прошел. Итак, я думаю, что я делаю что-то не так с MockResponse и его корпусом. Любая помощь будет высоко ценится.

Ответы [ 2 ]

0 голосов
/ 17 мая 2018

Метод buffer() в BufferedSource не считывает весь поток в этот буфер, чтобы вернуть его. Вместо этого он просто позволяет вам получить доступ к предварительно загруженным байтам из файла, который будет возвращен при следующем чтении.

Это код для загрузки файла в буфер:

val buffer = Buffer()
file.source().use {
  buffer.writeAll(it)
}
0 голосов
/ 16 мая 2018

По неясным для меня причинам Okio.buffer(Okio.source(file)).buffer() всегда возвращает пустое Buffer. Следующее, однако, работает:

mockWebServer.enqueue(MockResponse().setBody(Buffer().apply {
    writeAll(Okio.source(file))
}))

То, что я сейчас делаю, - это создание нового буфера вручную и запись в него всего файла. Теперь у моего MockResponse есть настоящее тело.

Я все еще хотел бы, чтобы кто-то объяснил , почему этого ....

...