Posted at

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

More than 1 year has passed since last update.

Microservices化などでRailsからも外部サービスへリクエストすることが増えてきました。

テストなどで実際に外部サービスへリクエストしてしまうとなにかとね、、

なので、Webmockを使ってスタブしてみましょう。

READMEにも書かれてますが、WebmockはRubyのHTTPライブラリをスタブするだけで、フロントからの外部リクエストをスタブすることができないので注意が必要です:persevere:

フロントからのリクエストをスタブしたい場合は、1日目に @chimame さんが書いてくれてるので参考にしてください:smile:

:eyes: featureスペックでクライアント(JavaScript)側から呼び出すRequestをモックする

この記事のサンプルコード 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がスタブする対象から除外することができます。

# スタブ登録されていないリクエストはすべて許可する

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 はJson Schemaを利用したRackミドルウェアです。

Rackミドルウェアなので、Webmockでも利用することをができます。


JsonSchema

webmock-sample/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٩( ‘ω’ )و