Mokelab Blog

#39 OkHttpのMockWebServerを試す

OkHttpにはテスト用のMockWebServerがあります。今回はこれを使ってテスト(Androidプロジェクト)を書いてみます。

公式ドキュメントはこちらにあります。

ライブラリを追加する

テストでしか使用しないので、testImplementationで追加します。アプリ本体で使用しているOkHttpとバージョンを合わせておきましょう。

dependencies {
    implementation 'com.squareup.okhttp3:okhttp:4.8.0'
    testImplementation 'com.squareup.okhttp3:mockwebserver:4.8.0'
}

テスト対象のコードを書く

ログインメソッドがあり、呼ぶとサーバーにPOSTリクエストを投げることにします。コンストラクタではbaseUrl として、ホストまでの部分(例えばhttps://api.example.com)を受け取ることにします。レスポンスの処理はサンプルなので見ていません。

class UserRepositoryImpl(
  private val client: OkHttpClient,
  private val baseUrl: String,
): UserRepository {
    override suspend fun login(email: String, password: String): User {
        val contentType = "application/json; charset=utf8".toMediaType()
        val json = "{\"email\":\"$email\",\"password\":\"$password\"}"
        val requestBody = json.toRequestBody(contentType)

        val request = Request.Builder().apply {
            post(requestBody)
            url("${baseUrl}/post")
        }.build()

        val response = withContext(Dispatchers.IO) {
            client.newCall(request).execute()
        }

        if (response.code != 200) {
            throw Exception("Error")
        }
        val body = withContext(Dispatchers.IO) {
            response.body?.string() ?: ""
        }
        // bodyの中身を見て、idとnameを取り出すのが正しいコード
        return User(id= "uid1", name = "Moke")
    }
}

モックサーバーを作って起動する

次はテスト側です。JVMテストで行うので、app/src/testの下にテストクラスを作ります。

class UserRepositoryImplTest {
    private lateinit var server: MockWebServer
    private lateinit var repo: UserRepositoryImpl

    @Before
    fun setup() {
        // インスタンスを作って
        server = MockWebServer()
        // レスポンスをセットして
        server.enqueue(MockResponse().apply {
            setResponseCode(200)
            setBody("{}")
            throttleBody(bytesPerPeriod = 10, period = 1, unit=TimeUnit.SECONDS)
        })
        // 起動!
        server.start()

        repo = UserRepositoryImpl(
            OkHttpClient(),
            server.url("").toString().dropLast(1)
        )
    }

    @After
    fun tearDown() {
        // 終わったらとめる
        server.shutdown()
    }
}

@Beforeでサーバーを起動し、@Afterでサーバーをとめるようにしています。server.enqueue() で、リクエストに対するレスポンスをセットします。テスト対象が複数回リクエストを投げる場合は複数個セットしておきましょう。 throttleBody()を使うと、レスポンスをゆっくり返してくれます。上記の例でば、10バイト送信する度に1秒待ちつつレスポンスを返します。

サーバーをstart()で起動した後は、テスト対象にbaseUrlを伝えます。server.url("")http://localhost:12345/のように、ルートへのパス(/)を含んだ文字列を返すので、dropLast(1)で末尾の/を抜いています。

テストを記述する

最後にテストメソッドです。

class UserRepositoryImplTest {
  private lateinit var server: MockWebServer
  private lateinit var repo: UserRepositoryImpl

  @Test
  fun login() {
      runBlocking {
          val user = repo.login("demo", "pass")
          val req1 = withContext(Dispatchers.IO) {
              server.takeRequest()
          }
          assertEquals("/post", req1.path)
      }
  }
}

テスト対象がsuspend funなのでrunBlockingを使います。 MockWebServerはtakeRequest()でどのようなリクエストを受け取ったか返してくれます。 ここではテスト対象のコードが/postを呼んだか確認しています。

まとめ

OkHttpのMockWebServerを使ってAndroidのJVMテストを書いてみました。テストのための機能は 一通り揃っているので、ぜひ使ってみてください。

本サイトではサービス向上のため、Google Analyticsを導入しています。分析にはCookieを利用しています。