TL;DR
MockRetrofit で異常系のテストを行う時は、 例外を throw せずに、 BehaviorDelegate#returning
にエラー内容を渡す。
※例外を throw すると、UnitTest が中断されてしまいます。
Good
class MockGitHubService(
private val delegate: BehaviorDelegate<GitHubService>
) : GitHubService {
override fun listRepos(user: String): Observable<List<Repository>> {
// 通信失敗を delegate する
val fakeCall = Calls.response(Response.error<Repository>(404, ResponseBody.create(...)))
return delegate.returning(fakeCall).listRepos(user)
}
}
Bad
class MockGitHubService(
private val delegate: BehaviorDelegate<GitHubService>
) : GitHubService {
override fun listRepos(user: String): Observable<List<Repository>> {
// ここで throw すると、UnitTest の実行が中断される
throw HttpException(Response.error<List<Repository>>(404, ResponseBody.create(...)))
}
}
背景
- API の呼び出しを Retrofit & RxJava で行っている
- 特定の例外なら、成功に丸め込んだり、別の例外を投げ直す
という事を行っており、正常系・異常系の UnitTest を書こうとしていました。
サンプルでおなじみの、GitHub API の呼び出しのコードで書くと、
service.listRepos("octocat")
.onErrorReturn { throwable ->
when {
// 404 なら空 list を返す(成功扱いにする)
(throwable is HttpException && throwable.code() == 404) -> listOf()
else -> throw throwable
}
}
こんなイメージで、これがテスト対象です。
正常系の MockService が、
override fun listRepos(user: String): Observable<List<Repository>> {
val fakeResponse = listOf(Repository(...), Repository(...)) // 2 件返す
return delegate.returningResponse(fakeResponse).listRepos(user)
}
のように記述できるので、 fun listRepos
で throw したら、 onErrorReturn
にひっかかる と思い込んでました。。。
(テストコードの全体感は、正常系のサンプルが色々出てくるのでそちらを参照下さい。)
まとめ
MockRetrofit で異常系のテストを行う時は、 例外を throw せずに、 BehaviorDelegate#returning
にエラー内容を渡す。