1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ユニークビジョン株式会社Advent Calendar 2024

Day 8

RSpec で oauth2 の API 呼び出しをモックする

Last updated at Posted at 2024-12-08

はじめに

OAuth 2.0 のトークンを使用して API にリクエストする機会が今までなかったのですが、先日、急に必要になったので困りました。ひとまずしのぎました。

そのついでに、RSpec で oauth2 のレスポンスをモックするにはどうすればよいのだろうと思ったので、試してみます。

X API v2 を題材にします。

oauth2 の gem を使用します。

Gemfile
gem "oauth2", "~> 2.0"

実際にリクエストを送信する

まず、実際にリクエストを送信して本物の結果を取得するコードを書いてみます。

spec/x_v2_oauth2_api_spec.rb
RSpec.shared_examples 'Actually sending the request' do
  describe '実際にリクエストを送信する処理が' do
    let(:oauth2_client) do
      options = { site: 'https://api.x.com' }
      OAuth2::Client.new(ENV.fetch('CLIENT_ID'), ENV.fetch('CLIENT_SECRET'), options)
    end

    let(:access_token) do
      OAuth2::AccessToken.from_hash(oauth2_client, { token: token_code })
    end

    it '成功すること' do
      response = access_token.get('/2/users/me')
      expect(response.parsed[:data][:id]).to eq '1111'
    end
  end
end

RSpec.describe 'XV2Oauth2Api' do
  let(:token_code) { 'aaa' }

  include_examples 'Actually sending the request'
end

OAuth2::AccessToken のインスタンスがリクエストを送信し、レスポンスを生成しています。このインスタンスをモックに差し替えることができれば、任意のレスポンスを返却させることができそうです。

リクエストとレスポンスをモックする

OAuth2::AccessToken クラスの from_hash メソッドを上書きするコードを用意します。

spec/oauth2_access_token_mock_context.rb
RSpec.shared_context 'with oauth2 access token mock' do
  let(:stub_access_token) do
    allow(OAuth2::AccessToken).to receive(:from_hash).and_return(mock_access_token)
  end

  let(:mock_access_token) do
    mock = instance_double('access_token')
    allow(mock).to receive(:get).and_return(OAuth2::Response.new(http_response))
    mock
  end

  let(:http_response) do
    mock = instance_double('http_response')
    allow(mock).to receive(:headers).and_return({ 'Content-Type' => 'application/json' })
    allow(mock).to receive(:status).and_return(200)
    allow(mock).to receive(:body).and_return(JSON.generate(response_data))
    mock
  end

  let(:response_data) do
    {
      data: {
        id: '1111',
        name: 'ユーザー1',
        username: 'user1'
      }
    }
  end
end

OAuth2::AccessToken の代わりのオブジェクトを生成し、自前で用意した OAuth2::Response インスタンスが返却されるようにしました。

使用してみます。

spec/x_v2_oauth2_api_spec.rb
RSpec.shared_examples 'Mocking an Access Token' do
  describe 'アクセストークンのモックが' do
    include_context 'with oauth2 access token mock'

    before do
      stub_access_token
    end

    let(:oauth2_client) do
      options = { site: 'https://api.x.com' }
      OAuth2::Client.new(ENV.fetch('CLIENT_ID'), ENV.fetch('CLIENT_SECRET'), options)
    end

    let(:access_token) do
      OAuth2::AccessToken.from_hash(oauth2_client, { token: token_code })
    end

    it '成功すること' do
      response = access_token.get('/2/users/me')
      # puts response.inspect
      expect(response.parsed[:data][:id]).to eq('1111')
    end
  end
end

RSpec.describe 'XV2Oauth2Api' do
  let(:token_code) { 'aaa' }

  include_examples 'Mocking an Access Token'
end

「実際にリクエストを送信する」のコードと同じように見えますが、リクエストは送信されず、自前で用意した結果が返却されます。

puts response.inspect で返却値を確認すると、InstanceDouble オブジェクトが返却されていることがわかります。

アプリケーションコード内で送信されるリクエストをモックする

アプリケーションコードが以下のように書かれているとします。

app/util/oauth2_util.rb
class Oauth2Util
  def self.make_client
    options = { site: 'https://api.x.com' }
    OAuth2::Client.new(ENV.fetch('CLIENT_ID'), ENV.fetch('CLIENT_SECRET'), options)
  end

  def self.make_access_token(token_code)
    client = make_client
    options = { token: token_code }
    OAuth2::AccessToken.from_hash(client, options)
  end
end
app/api/x_v2_oauth2_api.rb
class XV2Oauth2Api
  def initialize(access_token:)
    @access_token = access_token
  end

  def get_me
    @access_token.get('/2/users/me')
  end
end

独自クラス XV2Oauth2Api のインスタンスメソッドでリクエストが送信されています。その結果をモックできるかを確認します。

spec/x_v2_oauth2_api_spec.rb
RSpec.shared_examples 'Mocking access tokens created in application code' do
  describe 'アプリケーションコード内で作られているアクセストークンのモックが' do
    include_context 'with oauth2 access token mock'

    before do
      stub_access_token
    end

    let(:x_v2_oauth2_api) do
      access_token = Oauth2Util.make_access_token(token_code)
      XV2Oauth2Api.new(access_token: access_token)
    end

    it '成功すること' do
      response = x_v2_oauth2_api.get_me
      # puts response.inspect
      expect(response.parsed[:data][:id]).to eq('1111')
    end
  end
end

RSpec.describe 'XV2Oauth2Api' do
  let(:token_code) { 'aaa' }

  include_examples 'Mocking access tokens created in application code'
end

「リクエストとレスポンスをモックする」の shared_context をそのまま使用しています。beforeOAuth2::AccessToken クラスの from_hash メソッドが上書きされます。

こちらも、puts response.inspect でレスポンスがモックされていることが確認できます。

おわりに

OAuth 2.0 のトークンを使用したリクエストと、そのレスポンスをモックすることができました。

通信処理があるとテストが難しくなりますが、モックする方法が確立できているとテストコードが書きやすくなります。

テストコード、がんばります。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?