LoginSignup
4
1

More than 3 years have passed since last update.

【RSpec】stub_request(:post)でスタブ化したGoogle APIへのリクエストのレスポンスを実際と同一の形式にする

Last updated at Posted at 2020-06-29

タイトルが長い

RailsアプリケーションからGoogle Calendar APIでカレンダー上のイベントに対して更新処理を行い、そのレスポンスから特定の属性を取り出して取り回すような処理を追加した。
レスポンスで受け取ったJSONをgoogle-api-ruby-clientがGoogle::API::V3::Eventクラスのオブジェクトに変換してしまうため、スタブリクエストのレスポンスもruby-clientで変換させるか同様の構造を持ったオブジェクトにする必要があった。

TL;DR

  • google-api-ruby-clientを利用してGoogleAPIにリクエストした場合、レスポンスはJSON形式で返ってきたあとgoogle-api-ruby-clientで変換される
  • stub_requestからの戻り値のヘッダーに 'X-Goog-Upload-Status': 'final' を加えると実際のAPIからのレスポンスと同様の形式になる

スタブ化したリクエストと実際にAPIにリクエストした場合のレスポンスの違い

下記のコードはgoogle-api-ruby-clientを利用してリクエストし、レスポンスからイベントのIDを取得しようとしている

リクエスト

service = Google::Apis::CalendarV3::CalendarService.new
service.authorization = access_token

response = service.update_event('primary', event_id, body)
event_id = response.id

このとき response は以下のようになっている。

#<Google::Apis::CalendarV3::Event:0x000055c94da58f18
 @attachments=[],
 @attendees=[#<Google::Apis::CalendarV3::EventAttendee:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com", @organizer=true, @self=true>],
 @created=Mon, 01 Jan 2020 00:00:00 +0000,
 @creator=#<Google::Apis::CalendarV3::Event::Creator:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com", @self=true>,
 @description="",
 @end=#<Google::Apis::CalendarV3::EventDateTime:xxxxxxxxxxxxxxxxxxx>,
 @etag="xxxxxxxxxxxxxxxxxxx",
 @id="abcdefghijklmnopqrstu",
 @organizer=#<Google::Apis::CalendarV3::Event::Organizer:xxxxxxxxxxxxxxxxxxx @email="hoge@gmail.com">,
 @start=#<Google::Apis::CalendarV3::EventDateTime:xxxxxxxxxxxxxxxxxxx>,
 @updated=Mon, 01 Jan 2020 00:00:00 +0000>

このため、response オブジェクトから ゲッターメソッドの id で値を取得できる。

一方で大抵の場合、RSpec内でAPIレスポンスは下記のように定義されているだろう。

let(:response) do
  {
    id: 'abcdefghijklmnopqrstu',
    start: { date_time: Time.zone.today },
    end: { date_time: Time.zone.today },
    html_link: 'https://www.google.com/calendar/event?eid=xxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    i_cal_uid: 'xxxxxxxxxxxxxxxxxxx@google.com',
    summary: 'event name',
    updated: Time.zone.today,

    ...

  }.to_json
end

この場合イベントIDを取得するためには JSON.parse(response)['id'] のようにする必要があり、 response.id はNoMethodErrorとなる。

対応策

このJSON形式のレスポンスをEventクラスのオブジェクトとして返させるためには以下のように 'X-Goog-Upload-Status': 'final' をレスポンスのヘッダーに付与する必要がある。

stub_request(
  :put, url
).to_return(
  status: 201,
  body: response,
  headers: {
    content_type: 'application/json',
    'X-Goog-Upload-Status': 'final'
  }
)

google-api-ruby-clientのテストスペック内でもレスポンスに同様のヘッダー情報が付与されている

たまたま同僚がすぐに記事やGitHubのissueを見つけてくれたおかげで解決したけどそうじゃなかったら3日はハマってた気がする。

元ネタ

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1