Faraday の スタブテスト

2014.08.05

こんにちは、クラスメソッドの稲毛です。

Rails のコントローラ内で Faraday を使用する機会があり、RSpec の Example をどのように書けば良いのか調べてみました。

目標

架空の API(http://www.example.com/api/foobar) にスタブを設定し、リクエストによりそのレスポンスが得られる事を確認します。

Spec

スタブで置き換えれば良いのは Faraday.new で生成される Fraday::Connection オブジェクトということで、Spec ファイルにはスタブの Connection を let! で用意しました。Using Faraday for testing

    let!(:stub_connection) do
      Faraday.new do |conn|
        conn.adapter :test, Faraday::Adapter::Test::Stubs.new do |stub|
          stub.get('/api/foobar') do
            [ 200, {}, JSON.generate([ { id: 1, name: "Foo" }, { id: 2, name: "Bar" } ]) ]
          end
        end
      end
    end

通常は conn.adapter メソッドの引数として Faraday.default_adapter が用いられる所を、第一引数に :test 、第二引数に Faraday::Adapter::Test::Stubs オブジェクトとなっているのがスタブならではのようです。

      Faraday.new(url: 'http://www.example.com') do |conn|
        conn.request  :url_encoded
        conn.response :logger
        conn.adapter  Faraday.default_adapter
      end

Faraday::Adapter::Test::Stubs オブジェクトを生成する際のブロック内でリクエストに対応するレスポンスを指定します。ここでは GET メソッドで http://www.example.com/api/foobar がリクエストされたら JSON をボディとして HTTP ステータス 200 を返すようにしました。

          stub.get('/api/foobar') do
            [ 200, {}, JSON.generate([ { id: 1, name: "Foo" }, { id: 2, name: "Bar" } ]) ]
          end

リクエストパスによって異なるレスポンスを返すよう、複数のケースを設定することができます。

          stub.get('/api/foobar') do
            [ 200, {}, JSON.generate([ { id: 1, name: "Foo" }, { id: 2, name: "Bar" } ]) ]
          end
          stub.show('/api/foobar/1') do
            [ 200, {}, JSON.generate({ id: 1, name: "Foo" })]
          end
          stub.show('/api/foobar/2') do
            [ 200, {}, JSON.generate({ id: 2, name: "Bar" })]
          end
                                                :

Spec ファイルは以下のようになりました。

foobar_controller_spec.rb
require 'rails_helper'

RSpec.describe FoobarController, :type => :controller do
  describe 'GET index' do
    let!(:stub_connection) do
      Faraday.new do |conn|
        conn.adapter :test, Faraday::Adapter::Test::Stubs.new do |stub|
          stub.get('/api/foobar') do
            [ 200, {}, JSON.generate([ { id: 1, name: "Foo" }, { id: 2, name: "Bar" } ]) ]
          end
        end
      end
    end
    let(:request) { get :index, format: :json }

    before do
      allow(controller).to receive(:connection).and_return(stub_connection)
      request
    end

    it { expect(response).to be_success }
  end
end

before ブロックでコントローラの connection メソッドに対して stub_connection を設定しています。RSpec Mocks 3.0

Controller

Spec では、コントローラの connection メソッドをスタブと置き換える事を想定したので、コントローラでの Faraday.new は connection メソッドで返されるように実装します。

コントローラは以下のようになりました。

foobar_controller.rb
class FoobarController < ApplicationController
  def index
    response = connection.get '/api/foobar'
    p response.body # レスポンスボディをコンソールへ出力
    render nothing: true
  end

  private
    def connection
      Faraday.new(url: 'http://www.example.com') do |conn|
        conn.request  :url_encoded
        conn.response :logger
        conn.adapter  Faraday.default_adapter
      end
    end
end

結果

Spec を走らせた結果、レスポンスボディがスタブで指定したものになっていることが確認できました。

"[{\"id\":1,\"name\":\"Foo\"},{\"id\":2,\"name\":\"Bar\"}]"