背景
middlewareといっても、基本はinitializeとcallの2つのメソッドだけのclass。
rackを継承してるわけでもないので、適当なアプリをmiddlewareに食わせてテストを書こうかなぁ…
と悩んでいると、いい記事を見つけた。
Rack MiddlewareのGemに対するRspecの書き方
やってることは、
- callされたら適当なレスポンス返すだけのシンプルなアプリをテスト用に定義しておく
- rackアプリをこれに食わせてnewしておく
- Rack::Test::Methodsをincludeする
すると、Railsのcontrollerのテストのように、リクエストを投げて、そのレスポンスを使ってテストを書くことができる。
spec
先日作成したrack middlewareのテストを書いてみる。
middlewareはrack middlewareをざっくり触ってみたを参照。
callされたら適当なレスポンス返すだけのシンプルなアプリをテスト用に定義しておく
module TestApplicationHelper
extend self
class TestApplication
def call(env)
code = 200
body = [ "test body" ]
header = { "Content-Type" => "text/html;charset=utf-8",
"Content-Length" => "9",
"X-XSS-Protection" => "1; mode=block",
"X-Content-Type-Options" => "nosniff",
"X-Frame-Options" => "SAMEORIGIN" }
[ code, header, body ]
end
end
end
rackアプリをこれに食わせてnewしておく & Rack::Test::Methodsをincludeする
参照した記事にもあるが、appでrack_middlewareが返るようになっていれば、
HTTPリクエストを使ったテストができるようになる。
describe MyRackMiddleware do
include TestApplicationHelper
include Rack::Test::Methods
let(:test_app) { TestApplicationHelper::TestApplication.new }
let(:app) { MyRackMiddleware.new(test_app) }
describe "GET '/hoge'" do
it 'should return 200 OK' do
get '/hoge'
expect(last_response.status).to eq 200
expect(last_response.body).to eq html
expect(last_response.header["Content-Type"]).to eq "text/html;charset=utf-8"
expect(last_response.header["Content-Length"].to_i).to be > 0
expect(last_response.header["X-XSS-Protection"]).to eq "1; mode=block"
expect(last_response.header["X-Content-Type-Options"]).to eq "nosniff"
expect(last_response.header["X-Frame-Options"]).to eq "SAMEORIGIN"
end
end
end
last_request & last_response
Rack::Test::Methodsでリクエストを送信すると、last_request、last_responseそれぞれに値が格納される。
上記のテストのようにこれを使ってテストを書く。
last_request
テスト中で最後に送信したリクエスト。
- middlewareのcallで受け取るenvがそのままリクエストに入っている
- middlewareでリクエストを上書いた場合や追加した場合はlast_requestに反映される
- bodyはrack.inputに格納されている (別記事参照)
last_request.class #=> Rack::Request
last_request.env.class #=> Hash
last_request.env #=>
{ "rack.version" => [1, 2],
"rack.input" => #<StringIO:0x00000002a17708>,
"rack.errors" => #<StringIO:0x00000002a17f00>,
"rack.multithread" => true,
"rack.multiprocess" => true,
"rack.run_once" => false,
"REQUEST_METHOD" => "GET",
"SERVER_NAME" => "example.org",
"SERVER_PORT" => "80",
"QUERY_STRING" => "",
"PATH_INFO" => "/hoge",
"rack.url_scheme" => "http",
"HTTPS" => "off",
"SCRIPT_NAME" => "",
"CONTENT_LENGTH" => "0",
"rack.test" => true,
"REMOTE_ADDR" => "127.0.0.1",
"HTTP_HOST" => "example.org",
"HTTP_COOKIE" => "" }
last_response
テスト中で最後に受信したレスポンス
- RackのMockReponseが返ってくる
- status, body, headersへのaccessorが付いている
- Content-Lengthはlengthでアクセスできる
last_response.class #=> Rack::MockResponse
last_response.body #=> "hogehoge"
last_response.errors #=> ""
last_response.headers #=>
{ "Content-Type" => "text/html;charset=utf-8",
"Content-Length" => "8",
"X-XSS-Protection" => "1; mode=block",
"X-Content-Type-Options" => "nosniff",
"X-Frame-Options" => "SAMEORIGIN" }
last_response.length #=> 8
last_response.status #=> 200
ちなみに、Rack::Test::Methodsでリクエストを送信前にlast_request、last_responseにアクセスするとエラーになる。
last_request #=> Rack::Test::Error: No request yet. Request a page first.
last_response #=> Rack::Test::Error: No response yet. Request a page first.