ユーザ用ツール

サイト用ツール


サイドバー

プログレス合同会社

広告

android:studio:application:okhttp-test

22.OkHttpの単体テスト

OkHttpの実装リポジトリOkHttpHtmlRepositoryImplの単体(JVM上で行う)テストを行います。
※単にサイトからHTMLを取得するだけですので、単体テストの必要性はほぼありません。

単体テストには下記のライブラリモジュールを使用します。

  • kotlinx.coroutines.test (コルーチンテスト用ライブラリ)
  • mockk (Kotlin向けのモックライブラリ)
  • okhttp.mockwebserver (OkHttp用HTTPモックサーバー)

バージョンカタログファイルにOkHttpのバージョン定義を追記します。

[versions]
   :
# Test
kotlinx-coroutines-test = "1.10.2"  # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-test
mockk = "1.14.6"                    # https://mvnrepository.com/artifact/io.mockk/mockk

[libraries]
   :
# Test
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
   :

3行目~5行目[versions]
コメント部分のURLを参照して、最新安定バージョンを指定します。
9行目~12行目[libraries]
ライブラリモジュールとバージョンを関連付けします。

追記後、『Sync Now』で内容をプロジェクトに反映させます。

テスト用モジュールの参照

OkHttpのテストに必要なライブラリを:core:dataモジュールのbuild.gradle.ktsに追記します。

plugins {
   :
  id("de.mannodermaus.android-junit5")
}
   :
dependencies {
   :
  // ローカル単体テスト (src/test)
  testImplementation(libs.bundles.junit5)
  testRuntimeOnly(libs.junit5.engine)
  testImplementation(libs.kotlinx.coroutines.test)
  testImplementation(libs.mockk)
  testImplementation(libs.okhttp.mockwebserver)
}

3行目
JUnit 5のプラグインを追加します。
9行目~10行目
JUnit 5の依存モジュールを追加します。
11行目~13行目
OkHttpテスト用の依存モジュールを追加します。

追記後、『同期アイコン』で内容をプロジェクトに反映させます。

テスト用クラスの作成

:core:dataモジュールのTestにテスト用クラスを作成します。
ファイル名をOkHttpHtmlRepositoryImplTestにしています。

package jp.co.example.android01.core.data.repository

import kotlinx.coroutines.test.runTest
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
import jp.co.example.android01.core.data.repository.OkHttpHtmlRepositoryImpl

class OkHttpHtmlRepositoryImplTest {

  private lateinit var mockWebServer: MockWebServer
  private lateinit var repository: OkHttpHtmlRepositoryImpl

  @BeforeEach
  fun setUp() {
    mockWebServer = MockWebServer()
    mockWebServer.start()
    repository = OkHttpHtmlRepositoryImpl()
  }

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

  // 成功時_HTMLコンテンツを返す
  @Test
  fun getHtmlContent_Test200() = runTest {
    // Given
    val expectedHtml = """
            <!DOCTYPE html>
            <html>
            <head>
                <title>Test Page</title>
            </head>
            <body>
                <h1>Hello World</h1>
                <p>This is a test page.</p>
            </body>
            </html>
        """.trimIndent()

    mockWebServer.enqueue(
      MockResponse()
        .setResponseCode(200)
        .setBody(expectedHtml)
        .addHeader("Content-Type", "text/html; charset=utf-8")
    )
    // When
    val url = mockWebServer.url("/test").toString()
    val result = repository.getHtmlContent(url)
    // Then
    println("=== Test200 Result ===")
    println("Success: ${result.isSuccess}")
    println("Content: ${result.getOrNull()}")
    println("Expected: $expectedHtml")
    println("=====================")
    assertTrue(result.isSuccess)
    assertEquals(expectedHtml, result.getOrNull())
  }

  // 404エラー時_失敗を返す
  @Test
  fun getHtmlContent_Test404() = runTest {
    // Given
    mockWebServer.enqueue(
      MockResponse()
        .setResponseCode(404)
        .setBody("Not Found")
    )
    // When
    val url = mockWebServer.url("/notfound").toString()
    val result = repository.getHtmlContent(url)
    // Then
    println("=== Test404 Result ===")
    println("Success: ${result.isSuccess}")
    println("Failure: ${result.isFailure}")
    println("Exception: ${result.exceptionOrNull()?.message}")
    println("=====================")
    assertTrue(result.isFailure)
    assertTrue(result.exceptionOrNull()?.message?.contains("HTTP Error: 404") == true)
  }

  // 500エラー時_失敗を返す
  @Test
  fun getHtmlContent_Test500() = runTest {
    // Given
    mockWebServer.enqueue(
      MockResponse()
        .setResponseCode(500)
        .setBody("Internal Server Error")
    )
    // When
    val url = mockWebServer.url("/error").toString()
    val result = repository.getHtmlContent(url)
    // Then
    assertTrue(result.isFailure)
    assertTrue(result.exceptionOrNull()?.message?.contains("HTTP Error: 500") == true)
  }

  // 空のレスポンス時 空文字列を返す
  @Test
  fun getHtmlContent_TestEmpty() = runTest {
    // Given
    mockWebServer.enqueue(
      MockResponse()
        .setResponseCode(200)
        .setBody("")
    )
    // When
    val url = mockWebServer.url("/empty").toString()
    val result = repository.getHtmlContent(url)
    // Then
    assertTrue(result.isSuccess)
    assertEquals("", result.getOrNull())
  }

  // 大きなHTMLコンテンツ時_正常に処理される
  @Test
  fun getHtmlContent_TestLarge() = runTest {
    // Given
    val largeHtml = buildString {
      append("<!DOCTYPE html><html><head><title>Large Page</title></head><body>")
      repeat(1000) { i ->
        append("<p>This is paragraph number $i with some content.</p>")
      }
      append("</body></html>")
    }

    mockWebServer.enqueue(
      MockResponse()
        .setResponseCode(200)
        .setBody(largeHtml)
        .addHeader("Content-Type", "text/html; charset=utf-8")
    )
    // When
    val url = mockWebServer.url("/large").toString()
    val result = repository.getHtmlContent(url)
    // Then
    assertTrue(result.isSuccess)
    assertEquals(largeHtml, result.getOrNull())
    assertTrue((result.getOrNull()?.length ?: 0) > 10000)
  }
}

17行目~22行目
テスト開始前に実行する処理を定義しています。
mockWebServerを起動して、テスト対象のリポジトリを設定しています。
24行目~27行目
テスト終了後に実行する処理を定義しています。
mockWebServerを停止しています。
29行目~63行目
テストを実行する処理を定義しています。
46行目~51行目
mockWebServerからの応答を定義しています。
61行目~62行目
テストが成功した条件を定義しています。

テストの実行

テストの実行は、Android Studioターミナルで行います。

./gradlew :core:data:testDebugUnitTest --info
./gradlew :core:data:testDebugUnitTest --info --tests "OkHttpHtmlRepositoryImplTest"
./gradlew :core:data:testDebugUnitTest --info --tests "OkHttpHtmlRepositoryImplTest.getHtmlContent_Test200"

1行目
:core:dataに定義しているすべてのテストを行います。
2行目
OkHttpHtmlRepositoryImplTestに定義しているすべてのテストを行います。
3行目
OkHttpHtmlRepositoryImplTest内のgetHtmlContent_Test200のみテストを行います。

実行結果は、/core/data/build/reports/tests/testDebugUnitTest/内に作成されています。
index.htmlを開きます。

android:studio:application:android-app2201.png

実行結果

index.htmlを開くと、ブラウザのアイコンが表示されます。

クリックしてブラウザを開きます。

android:studio:application:android-app2202.png

サマリー

ブラウザでサマリーが表示されます。

テストしたパッケージをクリックします。

android:studio:application:android-app2203.png

クラス

パッケージ内のテスト用クラスが表示されます。

テストしたクラスをクリックします。

android:studio:application:android-app2204.png

標準出力

テスト関数内でprintlnした内容が、Standard outputタブ内に表示されます。

android/studio/application/okhttp-test.txt · 最終更新: by プログレス合同会社