外部APIのアクセスをするライブラリとか作成するとき、
毎回、APIをコールせず済むようになります.
今回、試したのは以下の環境です.
ライブラリ
WebMock
WebMockは名前の通りhttp(s)のモックです.
これでHTTP通信を差し替えます.
VCR
VCRはモックやスタブのデータを実際のHTTP通信から記録するライブラリです.
多分、ビデオカセットレコーダー(Video Cassette Recorder)の略
テスト対象のクラス
テスト対象として以下の様なAPIアクセスのクラスを作ったとします.
これのテストを書いていきます.
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
導入
とりあえず幾つか設定する必要があります.
Gemfile
とspec/rails_helper.rb
に以下を追記します.
group :development, :test do
# web mock
gem 'webmock', require: false
gem 'vcr', require: false
end
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
で管理しているので編集しておきます.
test:
example_provider_id: <%= ENV['EXAMPLE_PROVIDER_ID'] %>
example_api_key: <%= ENV['EXAMPLE_API_KEY'] %>
テストコード
テストコードはこんな感じになります.
c.configure_rspec_metadata!
が指定してあると、describe
やcontext
のメタデータでvcr: true
を指定すると初回時にHTTP通信をして記録します.
2回目以降は記録したHTTPのダンプデータを使うようになります.
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
を編集します.
テスト環境に環境変数を指定するのもいいですが、今回はデフォルト値で置き換えてしまいます.
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
を使っているので以下のようにしてみました.
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
を指定することで除外できます.
VCR.configure do |c|
...
c.ignore_localhost = true
end
感想
思ったより手軽に実装できました.
自分の場合、いろいろ検討した結果RSpecを使い続けることにしました.
RSpecで変態的なテストコードも書けますが、できる限りシンプルにテストを実装できるようにしています.
ライブラリ等のテストは使い方も兼ねるように書き書くため、RSpecだとsubjectにまとめるようにしています.