Madogiwa Blog

主に技術系の学習メモに使っていきます。

Rackアプリケーションのテストでリクエストをモックする方法

オレオレフレームワークを作っていると自作の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"]] }

通常RailsSinatraといったアプリケーションもこの基準に沿って作られています 🚃 🎩

Rackアプリケーションをテストしたい

それでは本題です、オレオレフレームワークを作っているとRackアプリケーションを自然と作っていくことになると思います。 Rackはシンプルなルールに沿っているため基本的にはテストしやすいはずですが、 最初に記載した「どうやってリクエストをモックすればいいか🤔」という問題が発生するわけです。

サンプルアプリケーションを作ってcapybra等を使って実際にブラウザを使ってアクセスすることも出来るかもしれないですが、 そこまでするのはコストも掛かるし、せっかくRackのシンプルな仕様があるので基本的なRackアプリケーションの動きはClassのテストを書きたいですよね。

ではどうやってリクエストをモックしてRackアプリケーションの返り値の検証をすればよいのでしょうか?

結論から言うとRack::MockRequestという標準ライブラリを使用することでリクエストをモックすることができます🙌

www.rubydoc.info

コードで書くと下記のような形です👩‍💻

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アプリケーションの内部挙動を検証する文にはリクエストをモックしたテストのほうが高速だし手軽ですね👩‍🏭

参考

medium.com

stackoverflow.com