webmockとは
HTTPリクエストをstubするgemです。
Net::HTTPだけでなく、TCPSocketを使った場合もstubしてくれます。
以前、sham_rack使ってみたという記事を書きましたが、
sham_rackはNet::HTTPのstubしかしてくれないのです。
TCPSocketをstubしてくれるgemを探していたところ、このgemに出会いました。
何ができるか
HTTPリクエストをリクエストホストに対して投げる前にフックして、あたかも処理されたようにレスポンスを返すことができる。
つまり、StubやMockのようなことができる。
しかも、Net::HTTPだけでなくTCPSocketも対応してくれる。
=> HTTPClientやExconもstubできることに!
有名なgemなので、いろんな方が記事にされていたりしますが、テスト中のリクエストのstubに利用するケースが多いようです。
使い方
(テストでの使い方は記事にされている方がたくさんいるのでそちらを参照してください)
基本的には、WebMock::APIを該当クラスにincludeすることでstubを登録できるようになります。
しかし、これだけだと、stubを登録しないとエラーになりますので注意してください。
stubを登録せずにリクエストするとエラーになる
require 'webmock'
include WebMock::API
res = Net::HTTP.get("www.example.com", "/") #=> WebMock::NetConnectNotAllowedError
__END__
具体的には以下のようなエラーメッセージが出力される。
/home/aries/.rvm/gems/ruby-2.1.5@ironna/gems/webmock-1.21.0/lib/webmock/http_lib_adapters/net_http.rb:114:in `request': Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/ with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} (WebMock::NetConnectNotAllowedError)
You can stub this request with the following snippet:
stub_request(:get, "http://www.example.com/").
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
stub_request (stubするリクエストの登録)
stub_requestで登録する。
第一引数にメソッド名(:anyにすると何でも)、第二引数にホスト名を指定する。
require 'net/http'
require 'webmock'
include WebMock::API
stub_request(:any, "www.example.com").to_return(
:body => "test ok",
:status => 200,
:headers => { 'Content-Length' => 7 }
)
res = Net::HTTP.get("www.example.com", "/")
p res #=> "abc"
allow_net_connect! (stub登録したホスト以外へのアクセスは許可する)
require 'webmock'
include WebMock::API
WebMock.allow_net_connect!
stub_request(:any, "www.example.com").to_return(
:body => "test ok",
:status => 200,
:headers => { 'Content-Length' => 7 }
)
http = HTTPClient.new
res = http.get "http://www.example.com/"
p res.status #=> 200
p res.body #=> "test_ok"
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body #=> GoogleのトップページのHTML
enable!/disable! (WebMockのon/off切り替え)
この機能が一番使いたかったやつ!!
require 'httpclient'
require 'webmock'
include WebMock::API
## includeしてstub_requestした時点でWebMockは有効になっている
stub_request(:any, "www.google.co.jp").to_return(
:body => "mock response",
:status => 200,
:headers => { 'Content-Length' => 13 }
)
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body[0..30] #=> "mock response"
WebMock.disable! #=> 無効化
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body #=> Googleのトップページ
WebMock.enable! #=> 有効化
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body #=> "mock response"
reset! (登録したstubのリセット)
require 'httpclient'
require 'webmock'
include WebMock::API
WebMock.allow_net_connect!
stub_request(:any, "www.google.co.jp").to_return(
:body => "mock response",
:status => 200,
:headers => { 'Content-Length' => 13 }
)
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body #=> "mock response"
WebMock.reset! #=> stub_requestで登録したStubがリセットされる
http = HTTPClient.new
res = http.get "http://www.google.co.jp/"
p res.status #=> 200
p res.body #=> Googleのトップページ
stub_request().to_rack (rackミドルウェアを登録することもできる)
FbGraphを使った、GraphAPIへの/meリクエストをWebMockでmockしてみた。
require 'fb_graph'
require 'dotenv'
require 'nenv'
require 'webmock'
include WebMock::API
WebMock.allow_net_connect!
Dotenv.load
class MyRackApp
def call(env)
[ 500, {}, [ "{}" ] ]
end
end
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
if request.path == "/me"
test_data = { "name" => "Test User" }.to_json
[ 200, { "Content-Type" => "application/json" }, [ test_data ] ]
else
@app.call(env)
end
end
end
app = MyRackApp.new
app = MyMiddleware.new(app)
stub_request(:any, /graph\.facebook\.com/).to_rack(app)
user = FbGraph::User.me(Nenv.access_token).fetch
p user.name #=> "Test User"