Rack Middlewareのリダイレクト設定をrspecでテストする

こんばんは,@h3_potetoです.

RailsにはRack Middlewareという,普通にアプリケーションを作っていると,「ちょっと触らないけど見覚えあるな」というものがあります.
Rack Middlewareといのは,Railsアプリケーションに到達する手前に挟み込まれているミドルウェアです.

そのため,アプリケーションの仕様ではないものだけれど,サーバーの設定ではないところに積みたいときなどに良く使います.


最たるものが,サイトの移行時におけるリダイレクト設定などでしょう.

アプリケーションの仕様として必要なものではないけれど,なだらかに移行するために一時的に組んでいたりするものが多いですよね.

でもそれって,将来的には必要ないものだし,できれば切り離して管理したい……でもnginxの設定に組み込むとなると,そっちだけ別の管理方法を考えなきゃいけない……


リダイレクト設定を組んだ

というわけで,リダイレクトの設定をRack Middlewareに組み込みました.
設定しようと思えば,Railsのルーティングでも,nginxのリダイレクト設定でも構わないのですが,上記のような理由によりRack Middlewareに積むことにしました.

Rack Middlewareを作成

まず,lib/middlewares/request_redirect.rbというようなファイルを作ります.
例として,旧サイトの/3reasonというアドレスを/reasonsにリダイレクトさせてみましょう.

class RequestRedirect
  def initialize(app)
    @app = app
  end

  def call(env)
    request = Rack::Request.new(env)
    case request.path
    when /^\/3reason($|\/.*)/
      return [301, {'Location' => "/reasons"}, self]
    end
    @app.call(env)
  end

  def each(&block)
  end
end


これで完成ですが,これをRailsアプリケーションで読み込んでやる必要があります.

Rack Middlewareの読み込み


config/application.rbに以下を追記します.

# coding: utf-8
## 中略
module TestApplication
  class Application < Rails::Application
    
    config.autoload_paths << "#{config.root}/lib/middlewares"

    ## 中略
  end
end

上記でRack Middlewareを作成した場所をautoload_pathsに設定してやります.

これで,/3reasonにアクセスすると見事にリダイレクトされます.



RSpecでテストが組みたい

これ,リダイレクトの設定が上手く作動しているかのテストなんて,そんな面倒なことをいちいちチェックしたくないですよね.

というわけで当然のごとくテストを組むわけです.


ただし,Rspec側で標準で用意してくれているものではないので,全部自作する必要があります.

require "spec_helper"

describe RequestRedirect do
  include Rack::Test::Methods

  let(:test_app) { ->(env) {[200, env, "app"] }}
  let(:app) { RequestRedirect.new(test_app) }

  describe "/reasons redirect" do
    it "/3reason should redirect to /reasons" do
      get("/3reason")
      (last_response.header["Location"]).should eq("/reasons")
    end
  end
end

まず,Rack::Test::Methodsをincludeしておく必要があります.これは後ほどlast_responseを使えるようにするためです.
そして,RequestRedirectに食わせるダミーのアプリを適当に生成してやります(test_app).
今回はLocationの書き換えをするだけなので,bodyの中身は適当に"app"とかにしておきました.

last_responseというのが肝心な判定部分ですが,Rack::Test::Methodsでリクエストを送信していると,last_requestlast_responseというのが使えます.

last_requestというのが,最後に送信しているリクエストです.
つまり,test_appで作成したものをRequestRedirectに噛ませてget("/3reason")していますが,このときに渡しているリクエストですね.
last_responseは,そこから最後に受信するレスポンスです.
つまり,今回の場合であればRequestRedirectから返ってきたレスポンスです.

参考


Ruby - rack middlewareのテストをrspecで書いてみた - Qiita


Rack MiddlewareのGemに対するRspecの書き方 -