Ruby
RSpec
stripe

テストしやすいクレジットカード決済をStripe+RSpecで作る

クレジットカード決済に関わる開発って神経を使いますよね。なるべく精神衛生を保ちながら開発を続けるためには、書ける限りテストコードは書きたいところです。

2016年10月に日本でも正式リリースしたStripeは、モダンな感じのインターフェイスのAPIで、決済機能の開発コストをだいぶ下げてくれます。今回はStripeとそのRuby用クライアントライブラリであるstripe-ruby、そしてRSpecを用いて、テストのしやすいクレジットカード決済の開発を模索します。

方法1) Stripeのテスト環境にそのままアクセスする

  • この上なくシンプルで最も確実
  • 当然ながらRSpecを走らせるたびAPIアクセスが行われるので、何もかもこれでやると実行にべらぼうに時間がかかる

方法2) RSpecでMock/Stubを作る

  • 作るのが面倒な場合もある。例えばStripeからWebhookで送られてくるイベントとかどうしよう

方法3) stripe-mockを使う

  • stripe-mockは、Stripeが公式に提供しているMock用のHTTPサーバ
  • 返ってくるデータは完全に固定。そこが後述のstripe-ruby-mockとの違い。

stripe-mockのセットアップについての補足

基本的にREADMEにある通りbrew installするだけですが、ライブラリから呼び出すAPIの向き先をこのMockサーバにしたい場合、Ruby版ならばさらに以下の設定が必要。

Stripe.api_base = 'http://localhost:12111'

ホスト名、ポートはstripe-mockの起動設定によって変えてください。

方法4) stripe-ruby-mockを使う

  • stripe-ruby-mockはサードパーティ製のMockライブラリ
  • stripe-rubyからHTTPリクエストをする時の処理に割り込んで、Mockに処理をさせるようにしている。
  • WebhookもMock化できる(正確には、Webhookでリクエストされてくるイベントのオブジェクトをそれっぽく生成してくれる)
  • 先述のstripe-mockと異なり、POSTされてきたデータをメモリ上に保持しており、リクエストに応じて動的に変化してくれる

# 例えばStripeでCustomerオブジェクトを作ってそのIDをDB上の会員IDと紐付けるメソッドがあるとする
describe PaidService do
  let(:stripe_helper) { StripeMock.create_test_helper } # Mock用のtoken作るために使う必要ある
  before { StripeMock.start } # ここで、オンメモリのDBが作成される
  after { StripeMock.stop } # ここでDBのデータは破棄される

  let(:user) { User.create }
  let(:paid_service) { PaidService.new }

  describe '#register' do
    # ここでStripeにアクセスしてる
    subject { paid_service.register(user, stripe_helper.generate_card_token) }

    it { is_expected.to_not be_nil }
  end
end

便利なんだけど、少し苦労するところ

  • 無効なカード情報(例えば期限切れ)を与えてエラーを発生させたい時、Stripe提供のエラー用テストトークンを用いる事ができない。StripeMock.prepare_card_errorを用いなければならない。FixtureやFactoryとの相性が少し悪いかもしれない。
  • 意外としっかりしていて(?)、内部でデータの整合性をある程度チェックしている。例えば、Plan IDを用いてSubscriptionを作りたいとき、このMockライブラリのオンメモリのDBに登録されていないPlan IDを指定すると、エラーになる。ありがたい事かもしれないけれど、このDBのためのFixtureが欲しくなってくる。
  • でも自分でMock作ったりする手間は軽減される。特にWebhook周り。

方法5) fake_stripeを使う

  • fake_stripeも、サードパーティ製のMockライブラリ。やや方法は異なるが、stripe-rubyからのHTTPリクエストに割り込んでMockの値を返すのはstripe-ruby-mockと同じ
  • Stripe.jsから呼び出されるJS用のAPIのMockまで用意してあるのが特徴。ただし、現時点(2017年12月)で用意されているのは、v1, v2まで。v3はまだの模様。
  • stripe-ruby-mockと異なり、返されるレスポンスはほぼ固定。ただ、create系のAPIが呼ばれると、FakeStripeのクラスで保持しているカウントが増える。APIが実際にコールされた事のテストには便利なのかもしれない。以下は、FakeStripeのSpecにあった例
describe "POST /v1/customers" do
  it "increments the customer counter" do
    expect do
      Stripe::Customer.create
    end.to change(FakeStripe, :customer_count).by(1)
  end
end

結局、何がおすすめか?

人によって好みもあるかと思いますが、自分の場合、APIにリクエストするまでもない単体のテストでMockを使いたい、という場合、今のところstripe-ruby-mockを用いています。先述の通り、使い方に少し癖があるところもありますが、一度セットアップさえしてしまえば楽です。特にWebhook周りが助かります。
Stripeはこうしたサードパーティのライブラリも充実しているのが魅力ですね。Stripe自体に関しても、ドキュメントも充実していてAPIも使いやすく、カスタマイズ自由がきくのでおすすめですよ。皆さんも使ってみてください(決してStripeさんの回し者じゃないです)