WebMockとHTTPartyで request spec にハマった話

久しぶりの更新になります。 今年はいろいろアウトプットできるといいですね。。。

今回は仕事沼ったことを、軽く、雑に、ウォームアップ感覚で書いていきたいと思います。

実装したAPIに request specを書くためとき、処理の中で外部のAPIにリクエストを送ることがあるかと思います。 specの中で実際にリクエストを飛ばされると困るし、テストしずらいので webmock という便利なgemが用意されています。

github.com

webmockを使うことによってstubしたurlに対して、どんなレスポンスを返すか指定することができます

その上で今回はHTTPartyを使用してリクエストを送る想定で進めます。

github.com

例えばコントローラーにこんな処理があったとします(とても雑に)

def index
  response = HTTParty.get("https://somesampleapi.com/posts/1")
  render json: response, status: :ok
end

これに対してこんな感じにstub_requestしたテストを書くことが可能です。 

let(:exepcted_response) do
  {
    "userId": 1,
    "id": 1,
    "title": "hogehoge",
    "body": "fugafuga"
  }
end

it "外部からレスポンスを取得できること" do
  stub_request(:get, "https://somesampleapi.com/posts/1")
    .to_return(status: 200, body: exepcted_response.to_json)
  get posts_url

  expect(response).to have_http_status(:ok)
  expect(response.body).to eq exepcted_response.to_json
end

しかし、resopnse.body がなぜか空のhashを返してました。

Failure/Error: expect(response.body).to eq exepcted_response.to_json

expected: "{\"userId\":1,\"id\":1,\"title\":\"hogehoge\",\"body\":\"fugafuga\"}"
got: "{}"

controllerをbinding.pryで覗くと以下のようになっていました

def index
  response = HTTParty.get("https://somesampleapi.com/posts/1")
  binding.pry
  render json: response, status: :ok
end

[10] pry(#<PostsController>)> response.class
=> HTTParty::Response
[11] pry(#<PostsController>)> response.to_json
=> "{}"
[12] pry(#<PostsController>)> response
=> "{\"userId\":1,\"id\":1,\"title\":\"hogehoge\",\"body\":\"fugafuga\"}"

renderにresopnseを渡すと、to_jsonが内部で自動で呼ばれた気がします(うろ覚え。。) なのでrender で to_jsonされた結果空のハッシュが返ってくるようです。

解決方法

結果、webmockの方に解決方法書いてありました

github.com

Set appropriate Content-Type for HTTParty's parsed_response.

stub_request(:any, "www.example.com").to_return body: '{}', headers: {content_type: 'application/json'}

ヘッダーコンテンツの中身しっかり書いておいてね〜という内容でした。 ということで、stub_requstを修正して、もう一度responseの中身を確認してみました。

stub_request(:get, "https://somesampleapi.com/posts/1")
  .to_return(status: 201, body: exepcted_response.to_json, headers: {content_type: "application/json"})
[1] pry(#<PostsController>)> response
=> {"userId"=>1, "id"=>1, "title"=>"hogehoge", "body"=>"fugafuga"}
[2] pry(#<PostsController>)> response.class
=> HTTParty::Response
[4] pry(#<PostsController>)> response.to_json
=> "{\"userId\":1,\"id\":1,\"title\":\"hogehoge\",\"body\":\"fugafuga\"}"

今回は to_jsonで空が入ってこなかったですね。 なのでspec側も動くはず

Finished in 0.07161 seconds (files took 1.6 seconds to load)
1 example, 0 failures

🙌 🙌 🙌 🙌

WebMock側かHTTParty側の問題の切り分けは必要でしたが、結局ドキュメントを先に見ていればある程度早く解決できたかもしれないです。 Official Document を真っ先にみよう!

今回は調べきれてないですが、 HTTPartyの挙動も気になりますなぁ。。


久しぶりに書いてみて、用語の意味が曖昧だったり、説明しきれてない部分、自分の理解してない部分が書いていてどんどん出てきました。これ良い学習になるな!と思いつつも、ガチガチにやるとリタイアしそうなので、最初のうちは軽く、ゆるく、長くできるようなスタンスで進めたいな(願望