自分がしらなかっただけですが、便利だったので。
VCRは外部へのリクエストを記録し、テスト時に外部リクエスト呼び出しをモックしてれるツールです。
VCR自体はカセット(casset)と呼ばれるフィクスチャを記録/再生する機能が主で、モック自体は
webmock/faradayなどのモックライブラリを内部で使用しています。
例えば、下記のようなstripeのapi実行を伴うようなテストケースをVCR.use_cassetteという
ブロックの中実行すると、指定したパスにレクエスト/レスポンスを記録したymlを保存してくれます。
(そして2回目以降はymlを読んで、リクエストにマッチしたら記録されているレスポンスを返してくれます)
describe CancelOrder do
it 'updates status of order/history' do
order = create(:order, :with_payment, :fixed)
VCR.use_cassette 'stripe/refund' do
CancelOrder.new(order).execute
# charge = Stripe::Charge.retrieve(order.payment.id)
# charge.refund
end
expect(order.status).to eq ::Order.statuses[:canceled]
expect(order.order_histories.last.status).to eq ::Order.statuses[:canceled]
end
end
---
http_interactions:
- request:
method: get
uri: https://api.stripe.com/v1/charges/ch_16gwnzBPgBFgcl8jurjBMKFq
body:
encoding: US-ASCII
string: ''
headers:
X-Stripe-Client-User-Agent:
- '{"bindings_version":"1.23.0","lang":"ruby","lang_version":"2.1.4 p265 (2014-10-27)","platform":"x86_64-linux","engine":"ruby","publisher":"stripe","uname":"Linux
version 2.6.32-358.el6.x86_64 (mockbuild@c6b8.bsys.dev.centos.org) (gcc version
4.4.7 20120313 (Red Hat 4.4.7-3) (GCC) ) #1 SMP Fri Feb 22 00:31:26 UTC 2013"'
response:
status:
code: 200
message: OK
headers:
Stripe-Version:
- '2015-07-07'
body:
encoding: UTF-8
string: |
{
"id": "ch_16gwnzBPgBFgcl8jurjBMKFq",
"object": "charge",
"amount": 37591,
"amount_refunded": 37591,
"application_fee": null,
"balance_transaction": null,
….
まあ、これだけでも便利なのですが、そのままだと異なるデータを返したい場合に別のカセットを
定義しないといけなくなります。
また、APIによってはオブジェクトごとにリクエストのパラメーターが変わったりタイムスタンプが
追加されたりするので、そのままだとモックにマッチしなくなります。
※これはmatch_request_on: [:path]のようなかたちでパラメーターを渡すとマッチの条件を変更することで対応できますが(Matching on Path)
VCRはyaml内にerbも書けるので下記のようにタグを埋め込んで、呼び出し時にアサイン変数を渡せば
動的にレスポンスを変更できます。
Dynamic ERB cassettes
http://www.relishapp.com/vcr/vcr/v/3-0-3/docs/cassettes/dynamic-erb-cassettes
---
http_interactions:
- request:
method: get
uri: https://api.stripe.com/v1/charges/<%= charge_id %>
response:
status:
code: 200
message: OK
headers:
Stripe-Version:
- '2015-07-07'
body:
encoding: UTF-8
string: |
{
"id": "<%= charge_id %>",
"object": "charge",
"amount": <%= amount %>,
"amount_refunded": <%= amount %>,
"application_fee": null,
"balance_transaction": null,
….
describe CancelOrder do
it 'updates status of order/history' do
order = create(:order, :with_payment, :fixed)
VCR.use_cassette 'stripe/refund', erb: { charge_id: order.payment.charge_id, amount: 3000 } do
CancelOrder.new(order).execute
end
expect(order.status).to eq ::Order.statuses[:canceled]
expect(order.order_histories.last.status).to eq ::Order.statuses[:canceled]
end
end
※テストコードがあんまいい例じゃなくてすんません。
そもそもVCRを使うとあんまり外部ライブラリの実装を気にせず、スタビーなテストコードが減らせますが、
erbをつかうとさらに
- フィクスチャ(cassette)を数を抑制できる
- お手軽にAPIのレスポンスを変更できる
ので、いいかもしれません。