LoginSignup
38
36

More than 5 years have passed since last update.

VCRで外部APIダンプを作って使う

Last updated at Posted at 2015-04-02

外部APIのアクセスをするライブラリとか作成するとき、
毎回、APIをコールせず済むようになります.

今回、試したのは以下の環境です.

ライブラリ

WebMock

WebMockは名前の通りhttp(s)のモックです.
これでHTTP通信を差し替えます.

VCR

VCRはモックやスタブのデータを実際のHTTP通信から記録するライブラリです.
多分、ビデオカセットレコーダー(Video Cassette Recorder)の略

テスト対象のクラス

テスト対象として以下の様なAPIアクセスのクラスを作ったとします.
これのテストを書いていきます.

example_api.rb
require 'net/http'
require 'json'

class ExampleApi
  STATUS_URL = 'https://example.com/contents/status'

  def initialize(provider_id, api_key)
    @provider_id = provider_id
    @api_key = api_key
  end

  def status(content_ids)
    uri = URI.parse STATUS_URL
    uri.query = { pid: @provider_id, api_key: @api_key }

    res = post_http uri,
                    headers: { 'Content-Type' => 'application/json' },
                    body: { content_ids: content_ids }.to_json

    return JSON.parse(res.body, symbolize_names: true) if res.code == '200'
    err = JSON.parse res.body
    fail err['message']
  end

  def post_http(uri, options = {})
    ...
  end
end

導入

とりあえず幾つか設定する必要があります.
Gemfilespec/rails_helper.rbに以下を追記します.

Gemfile
group :development, :test do
  # web mock
  gem 'webmock', require: false
  gem 'vcr', require: false
end
spec/rails_helper.rb
require 'webmock/rspec'
require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'spec/vcr'
  c.hook_into :webmock
  c.configure_rspec_metadata!
end

APIのキーはsecrets.ymlで管理しているので編集しておきます.

config/secrets.yml
test:
  example_provider_id: <%= ENV['EXAMPLE_PROVIDER_ID'] %>
  example_api_key: <%= ENV['EXAMPLE_API_KEY'] %>

テストコード

テストコードはこんな感じになります.

c.configure_rspec_metadata!が指定してあると、describecontextのメタデータでvcr: trueを指定すると初回時にHTTP通信をして記録します.

2回目以降は記録したHTTPのダンプデータを使うようになります.

example_api_spec.rb
describe ExampleApi do
  subject { described_class.new provider_id, api_key }
  let(:provider_id) { Rails.application.secrets.example_provider_id }
  let(:api_key)     { Rails.application.secrets.example_api_key }

  describe '#status' do
    subject { super().status content_ids }

    # VCRを有効にいする
    context 'success', vcr: true do
      let(:content_ids) { %w(test_1 test_2) }

      it do
        should match(
          [
            { content_id: 'test_1', status: 'RUNNING' },
            { content_id: 'test_2', status: 'FINISHED' }
          ]
        )
      end
    end
  end
end

機密情報のフィルタリング

上記のテストではRails.application.secretsを使っているのでVCRで記録したときにspec/vcr以下に機密情報が含まれたyamlが作成されています.

このままではGit等のSCMで管理しにくいですね.
そこで機密情報をフィルタリングしていきます.

元ネタは以下にありました.

config/secrets.ymlを編集します.
テスト環境に環境変数を指定するのもいいですが、今回はデフォルト値で置き換えてしまいます.

config/secrets.yml
test:
  example_provider_id: <%= ENV['EXAMPLE_PROVIDER_ID'] || 'example_pid' %>
  example_api_key: <%= ENV['EXAMPLE_API_KEY'] || 'example_api_key' %>

そしてVRCのフィルタリングはfilter_sensitive_dataを使います.
今回はRailsのconfig/secrets.ymlを使っているので以下のようにしてみました.

spec/rails_helper.rb
VCR.configure do |c|

  ...

  Rails.application.secrets.each do |k, v|
    next unless v.is_a? String
    c.filter_sensitive_data("<#{k.upcase}>") { v }
  end
end

ローカルホストの接続を除外

CucumberやTurnipを使ってるときローカルホストを除外したい時があります.
ignore_localhostを指定することで除外できます.

spec/rails_helper.rb
VCR.configure do |c|

  ...

  c.ignore_localhost = true
end

感想

思ったより手軽に実装できました.

自分の場合、いろいろ検討した結果RSpecを使い続けることにしました.

RSpecで変態的なテストコードも書けますが、できる限りシンプルにテストを実装できるようにしています.

ライブラリ等のテストは使い方も兼ねるように書き書くため、RSpecだとsubjectにまとめるようにしています.

38
36
1

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
38
36