sttpとは
ScalaのHTTP Clientライブラリです。このライブラリの特徴の1つとして、リクエスト送信時にbackendが設定できるというのがあります。backendを設定するとが何がうれしいかというと、返り値をモナドにしたり、Stubをセットできるという点にあります。Stubを書けば、テスト用に実際にリクエストをせず、リクエストパターンに応じて決まった値を返すとかできて便利なんですね。
今回はこのStubをさわっていたら色々ハマりどころがあったのでメモを残しておきます。
やりたかったこと
とあるプロジェクトで、develop環境において特定サーバへのリクエストが必ずエラーになるため、Stubで決まった値を返そうとしていました。特定サーバ以外はもちろん今まで通り通信してほしいという要件です。ちなみにStubはDIを使って環境別に注入していて、分かってくるとそこそこDIも便利ですね。
問題点
さて、そのStubは最初以下のように書いてました
HttpURLConnectionBackend.stub
.whenRequestMatches(req => matchUri(req)).thenRespond(res)
.whenRequestAny.thenRespond(HttpURLConnectionBackend())
実際のところ、実行してみるとmatchしないパターンで ClassCastException
が出ます。Scalaではあまり見ないエラーですし、全然関係ないところで出てきたのでびっくりしました。
まず問題なのは thenRespond
にbackend渡しているところです。コンパイルエラーが出ないし大丈夫やろ、っていうScala開発スタイルに慣れ切っていたのでそういう雑なコード書いたんですが、内部的にCastしてるらしくて型が仕事してくれない。これはあまりイケてない仕様な気がします。
で、stubのメソッド一覧を目を皿のようにして見渡してみますが、それっぽい機能がなく、ライブラリのソースコードを読んでみたところ(これは大変読みやすかったです)、fallbackという引数が発覚します。あるやんけ!
しかしどっかのサイトで見たこのHttpURLConnectionBackend.stubという記法には欠点があって、このfallback引数に値を渡す方法がないらしいことも分かります。機能が制限されるAPIっていうのもイケてないですね…。
もうちょっとソースコードに目を通すと、どうもSttpBackendStubを直接呼び出すのが正解らしい。(公式ドキュメントにもそう書いてあった)というわけで最終解は以下です。
SttpBackendStub.withFallback(HttpURLConnectionBackend())
.whenRequestMatches(req => matchUri(req)).thenRespond(res)
蛇足
Sttpのエッジケースでハマったし微妙に再現性が高そうだったのでQiitaにメモっておきました。誰かの役に立つなら幸いです。個人的な反省点はコンパイル通すだけで満足せずに動作テストをしろってやつですね…。