オレオレフレームワークを作っていると自作のRackアプリケーションのテストを書くときに、 「どうやってリクエストをモックすればいいか🤔」という問題が立ちふさがるのですが、 Rackの標準ライブラリを使うといい感じに出来ることがわかったのでMEMO✍
そもそもRackアプリケーションとは?
Rackアプリケーションとは、引数を1つとり、返り値として3つの値を返すRuby Objectです💎
正確には下記のようにルールが定められています。
Rack applications A Rack application is a Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns an Array of exactly three values: The status, the headers, and the body. https://www.rubydoc.info/github/rack/rack/master/file/SPEC
最小構成のRackアプリケーションは下記のような形です。
Proc.new { |env| ['200', {'Content-Type' => 'text/html'}, ["Hello World"]] }
通常RailsやSinatraといったアプリケーションもこの基準に沿って作られています 🚃 🎩
Rackアプリケーションをテストしたい
それでは本題です、オレオレフレームワークを作っているとRackアプリケーションを自然と作っていくことになると思います。 Rackはシンプルなルールに沿っているため基本的にはテストしやすいはずですが、 最初に記載した「どうやってリクエストをモックすればいいか🤔」という問題が発生するわけです。
サンプルアプリケーションを作ってcapybra
等を使って実際にブラウザを使ってアクセスすることも出来るかもしれないですが、
そこまでするのはコストも掛かるし、せっかくRackのシンプルな仕様があるので基本的なRackアプリケーションの動きはClassのテストを書きたいですよね。
ではどうやってリクエストをモックしてRackアプリケーションの返り値の検証をすればよいのでしょうか?
結論から言うとRack::MockRequest
という標準ライブラリを使用することでリクエストをモックすることができます🙌
コードで書くと下記のような形です👩💻
describe '#call' do let(:app) { Proc.new { |env| ['200', {'Content-Type' => 'text/html'}, ["Hello World!"]] } } it 'Success Hellow World' do response = Rack::MockRequest.new(app).get('/') expect(response.status).to eq 200 expect(response.body).to eq 'Hello World!' end end
上記テストを実行した際に実際にRackアプリケーションの引数で渡されるenv
の値は下記のような形になります。
{ "rack.version"=>[1, 3], "rack.input"=>#<StringIO:0x00007fb90d124030>, "rack.errors"=>#<StringIO:0x00007fb90d124198>, "rack.multithread"=>true, "rack.multiprocess"=>true, "rack.run_once"=>false, "REQUEST_METHOD"=>"GET", "SERVER_NAME"=>"example.org", "SERVER_PORT"=>"80", "QUERY_STRING"=>"", "PATH_INFO"=>"/", "rack.url_scheme"=>"http", "HTTPS"=>"off", "SCRIPT_NAME"=>"", "CONTENT_LENGTH"=>"0" }
しかし完全にenv
が再現されるわけではなくenv['REQUEST_URI']
等は含まれていないので、
存在しない値に依存した実装になっていると上手くモックできないようです😢
おわりに
Rackのシンプルな仕様と便利なRack::MockRequest
という標準ライブラリを使うといい感じにリクエストをモックしてRackアプリケーションをテストすることができます 👍
複雑なレスポンス + js等のインタラクティブな動作を伴う挙動の検証はcapybra
等のブラウザを仕様したテストする必要がありますが、
シンプルなレスポンス及びRackアプリケーションの内部挙動を検証する文にはリクエストをモックしたテストのほうが高速だし手軽ですね👩🏭