【Rails】PAY.JPによる決済機能のモックを活用したコントローラーテスト

# はじめに

PAY.JPのAPIを用いた決済機能を実装した際にコントローラーの単体テストを実装しました。 PAY.JPを使用した機能においてテストを実施する場合、ダミーレスポンスを作る対応が必要とのことで、カード情報取得のレスポンスをモックで擬似的に再現したので実装方法をシェアします。

# 開発環境
Ruby 2.5.1
Rails 5.0.7.2
RSpec 4.0
factory_bot 5.1.1

# モックとは

モックは以下のような定義となります。

+ 本物のふりをする偽物のプログラム
+ テスト用の影武者として働くオブジェクト

モックの定義については下記記事を参考にさせていただきました。

[使えるRSpec入門・その3「ゼロからわかるモック（mock）を使ったテストの書き方」](https://qiita.com/jnchito/items/640f17e124ab263a54dd)
[【Ruby on Rails】RSpecにおけるモックとスタブの基本事項メモ【プログラミング学習開始142日目】](https://qiita.com/fuku_tech/items/261e958b35ab809213da)

# 実装手順

PAY.JPの導入が完了していることを前提としています。
PAY.JPの導入は下記記事を参考にさせていただきました。
[Payjpでクレジットカード登録と削除機能を実装する(Rails)」](https://qiita.com/takachan_coding/items/f7e70794b9ca03b559dd)

## テスト対象の機能

購入確認画面などでPAY.JPの機能で登録したクレジットカードの情報を取得し表示させたい場合の機能を対象としています。

<img width="417" alt="スクリーンショット 2020-01-31 20.10.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481946/c9aa59ea-0b95-7a1e-c4f9-d0a42a718c3f.png">


## コントローラーのサンプルコード

```app/controllers/purchase_controller.rb
  def show
    @image = @product.images.first
    Payjp.api_key = ENV["PAYJP_PRIVATE_KEY"]
    customer = Payjp::Customer.retrieve(@card.customer_id)
    @default_card_information = customer.cards.retrieve(@card.card_id)
    @address = Address.find_by(user_id: current_user)
  end
```

## モックのサンプルコード

Payjp::Customerのレスポンスを下記にように擬似的に作ります。
外部APIから取得しているトークンの内容を確認してモックからのレスポンスとして記述しています。
モックの作り方は、下記記事のPayjp::Charge.createのモックを参考にさせていただきました。
[Rails で Payjp を使って決済システムを導入する](https://qiita.com/hirotakasasaki/items/794c920016ac7c33da74#%E8%87%AA%E5%8B%95%E3%83%86%E3%82%B9%E3%83%88rspec)

```spec/support/payjp_mock.rb
module PayjpMock
  def self.prepare_customer_information # Payjp::Customerのレスポンスモック
    {
      "id": "cus_ca9d1d98900ec1f2595aebefd9a6",
      "cards": {
        "count":1,
        "data":[{
          "id":"car_a96c76b044d7ae21439d7b9840b7",
          "address_city":nil,
          "address_line1":nil,
          "address_line2":nil,
          "address_state":nil,
          "address_zip":nil,
          "address_zip_check":"unchecked",
          "brand":"Visa",
          "country":nil,
          "created":1578830630,
          "customer":"cus_ca9d1d98900ec1f2595aebefd9a6",
          "cvc_check":"passed",
          "exp_month":12,
          "exp_year":2020,
          "fingerprint":"e1d8225886e3a7211127df751c86787f",
          "last4":"4242",
          "livemode":false,
          "metadata":{},
          "name":nil,
          "object":"card"
          }],
          "has_more":false,
          "object":"list",
          "url":"/v1/customers/cus_ca9d1d98900ec1f2595aebefd9a6/cards"
        },
      "created": 1578830631,
      "default_card": "car_a96c76b044d7ae21439d7b9840b7",
      "description": nil,
      "email": nil,
      "livemode": false,
      "metadata": {},
      "object": "customer",
      "subscriptions": {
        "count":0,
        "data":[],
        "has_more":false,
        "object":"list",
        "url":"/v1/customers/cus_ca9d1d98900ec1f2595aebefd9a6/subscriptions"
      }
    }
  end
end
```

## モックの呼び出し

モックを使用したいテストコードで下記のようにモックを呼び出します。

```sample.rb
allow(Payjp::Customer).to receive(:create).and_return(PayjpMock.prepare_customer_information)
```

## コントローラーテストのサンプルコード

実際のテストコードでは下記のように呼び出します。
なお、本サンプルコードはfactorybotを使用しています。

```spec/controllers/purchase_controller_spec.rb
require 'rails_helper'
describe PurchaseController, type: :controller do
  let(:user) { create(:user) }
  let(:card) { create(:card, user_id: user.id) }
  describe 'GET #show' do
      context 'ログイン時' do
      before do
        login user
      end
      it "showアクションのページに遷移するか" do
        allow(Payjp::Customer).to receive(:create).and_return(PayjpMock.prepare_customer_information)
        product = create(:product)
        get :show, params: { id: product,card: card}
        expect(response).to render_template :show
      end
    end
  end
end
```

# まとめ
モックを用いることで外部APIからのレスポンスを擬似的に作ることできました。

# 参考記事
[使えるRSpec入門・その3「ゼロからわかるモック（mock）を使ったテストの書き方」](https://qiita.com/jnchito/items/640f17e124ab263a54dd)
[【Ruby on Rails】RSpecにおけるモックとスタブの基本事項メモ【プログラミング学習開始142日目】](https://qiita.com/fuku_tech/items/261e958b35ab809213da)
[Rails で Payjp を使って決済システムを導入する](https://qiita.com/hirotakasasaki/items/794c920016ac7c33da74#%E8%87%AA%E5%8B%95%E3%83%86%E3%82%B9%E3%83%88rspec)