Railsで外部リクエストをスタブする

Microservices化などでRailsからも外部サービスへリクエストすることが増えてきました。
テストなどで実際に外部サービスへリクエストしてしまうとなにかとね、、
なので、[Webmock](https://github.com/bblimke/webmock)を使ってスタブしてみましょう。

[README](https://github.com/bblimke/webmock#supported-http-libraries)にも書かれてますが、WebmockはRubyのHTTPライブラリをスタブするだけで、フロントからの外部リクエストをスタブすることができないので注意が必要です:persevere:

フロントからのリクエストをスタブしたい場合は、1日目に @chimame さんが書いてくれてるので参考にしてください:smile:
:eyes: [featureスペックでクライアント（JavaScript）側から呼び出すRequestをモックする](http://qiita.com/chimame/items/55f8369b539c28215ad9)

この記事のサンプルコード [murajun1978/webmock-sample](https://github.com/murajun1978/webmock-sample)

## リクエストをスタブする
Webmockにスタブするリクエストにレスポンスを設定して登録します。

```base_stub.rb
require 'bundler'
Bundler.require

require 'webmock'
require 'faraday'
include WebMock::API

WebMock.enable!

client = Faraday.new(url: 'http://www.example.com')

stub_request(:any, "www.example.com").to_return(
  body: 'success!',
  status: 200
)

res = client.get '/'

pp "status: #{res.status}, body: #{res.body}" #=> "status: 200, body: success!"
```

## マッチしたリクエストをスタブする
bodyやqueryなどマッチしたリクエストをスタブに登録します。

```matching_stub.rb
require 'bundler'
Bundler.require

require 'webmock'
require 'faraday'
include WebMock::API

WebMock.enable!

client = Faraday.new(url: 'http://www.example.com')

stub_request(:post, "www.example.com").with(
  body: { message: 'Hello' }
).to_return(
  body: 'success!',
  status: 201
)

res = client.post '/', { message: 'Hello' }
pp "status: #{res.status}, body: #{res.body}" #=> "status: 201, body: success!"
```

## 外部リクエストを許可する
WebmockはすべてのHttpリクエストをスタブするので、スタブ登録されていないリクエストはエラーになります。
Webmockがスタブする対象から除外することができます。

```rb
# スタブ登録されていないリクエストはすべて許可する
WebMock.allow_net_connect!

# localhostへのリクエストはすべて許可する
WebMock.disable_net_connect!(allow_localhost: true)

# 特定のホストへのリクエストはすべて許可する
WebMock.disable_net_connect!(allow: 'www.example.org')
```

## Rackミドルウェアを使ってスタブする
WebmockはRackミドルウェアを利用することができます。

##### JSON
```json/hello.json
{
  "message": "Hello Webmock!"
}
```

##### Rackミドルウェアを作成

```fake_rack.rb
require 'sinatra/base'

class FakeRack < Sinatra::Base
  post '/hello' do
    json_response 201, 'hello.json'
  end

  private

    def json_response(response_code, file_name)
      content_type :json
      status response_code
      File.open(File.dirname(__FILE__) + '/json/' + file_name, 'rb').read
    end
end
```

##### StubにRackミドルウェアを登録

```rack_stub.rb
require 'bundler'
Bundler.require

require 'webmock'
require 'faraday'
require 'faraday_middleware'
require_relative 'fake_rack'
include WebMock::API

WebMock.enable!

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

# rackミドルウェアを登録
stub_request(:any, /www.example.com/).to_rack(FakeRack)

res = client.post '/hello'

pp "status: #{res.status}, body: #{res.body}" #=> "status: 201, body: {\"message\"=>\"Hello Webmock!\"}"
```

## Rack::JsonSchemaを使ってスタブする
[Rack::JsonSchema](https://github.com/r7kamura/rack-json_schema) はJson Schemaを利用したRackミドルウェアです。
Rackミドルウェアなので、Webmockでも利用することをができます。

##### JsonSchema

[webmock-sample/examples/json/schema.json
](https://github.com/murajun1978/webmock-sample/blob/master/examples/json/schema.json)

##### StubにRackミドルウェアを登録

```rack_stub_with_rack_json_schema.rb
require 'bundler'
Bundler.require

require 'webmock'
require 'faraday'
require 'faraday_middleware'
require 'rack-json_schema'
include WebMock::API

WebMock.enable!

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

schema_file  = File.open(File.dirname(__FILE__) + '/json/schema.json', 'rb').read
local_schema = JSON.parse(schema_file)

mock_rack = Rack::Builder.new do
              use Rack::JsonSchema::Mock, schema: local_schema
              run ->(env) { [404, {}, ["Not Found"]] }
            end

stub_request(:any, /www.example.com/).to_rack(mock_rack)

res = client.get '/users'

pp "status: #{res.status}, body: #{res.body}" #=> "status: 200, body: [{\"id\"=>\"01234567-89ab-cdef-0123-456789abcdef\", \"name\"=>\"murajun1978\"}]"
```

## まとめ
今更感がハンパないですがまとめてみました:sweat_smile:
外部サービスのリクエストをまるっぽスタブしたいときはRackミドルウェアが便利ですね。
個人的にはJSONを使ってスタブするのがよさげでした。

Happy Hacking٩( ‘ω’ )و