SinatraでModularスタイルのときのテストでハマってた話
ざっくりまとめると
SinatraのModulerスタイルで書いたときは、RSpecなどでテストするときに上書きするRack::Testのappメソッドを
def app Sinatra::Application end
ではなく
def app HelloSinatra end
みたいに、アプリケーションのクラス(上記の場合はHelloSinatra)を返すようにしないとテストがうまく動かないという話です。
環境
なにごと?
Testing Sinatra with Rack::Testを見つつこんな感じのコードとテストを書いてたわけですよ。
# app.rb require 'sinatra/base' class HelloSinatra < Sinatra::Base get '/' do 'Hello, Sinatra!' end end
# spec/requests/hello_spec.rb ENV['RACK_ENV'] = 'test' require File.join(File.dirname(__FILE__), '..', '..', 'app.rb') require 'sinatra' require 'rack/test' require 'rspec' describe 'HelloSinatra' do include Rack::Test::Methods def app Sinatra::Application end it 'should be OK' do get '/' expect(last_response).to be_ok end end
で、テストすると失敗するわけです。
$ rspec F Failures: 1) HelloSinatra should be OK Failure/Error: expect(last_response).to be_ok expected ok? to return true, got false # ./spec/requests/hello_spec.rb:18:in `block (2 levels) in <top (required)>' Finished in 0.03914 seconds 1 example, 1 failure Failed examples: rspec ./spec/requests/hello_spec.rb:16 # HelloSinatra should be OK
なんでかなーと思ってModular vs. Classic Style - Sinatra: READMEを見つつ考えてみると、Testing Sinatra with Rack::TestのSinatra+RSpecのサンプルはClassicスタイルで
require 'sinatra' get '/' do 'Hello world!' end
って感じで書いてあります。 で、Classicスタイルだとconfig.ruは
require './app' run Sinatra::Application
のようにSinatra::Applicationを起動します。 これはきっとClassicスタイルの場合はルータを定義したりするとSinatra::Applicationが拡張されていくってことかな(TODO: ちゃんと調べる)。
それに対してModulerスタイルで
require 'sinatra/base' class HelloSinatra < Sinatra::Base get '/' do 'Hello, Sinatra!' end end
ってやるときは、config.ruは
require './app' run HelloSinatra
のように、アプリケーションのクラスから起動します。
…ということはModulerスタイルの場合、RSpecでもappメソッドはSinatra::ApplicationではなくHelloSinatraを返さなきゃダメなんじゃないだろうか、ということで
# spec/requests/hello_spec.rb ENV['RACK_ENV'] = 'test' require File.join(File.dirname(__FILE__), '..', '..', 'app.rb') require 'sinatra' require 'rack/test' require 'rspec' describe 'HelloSinatra' do include Rack::Test::Methods def app HelloSinatra # ←Sinatra::Applicationから変更 end it 'should be OK' do get '/' expect(last_response).to be_ok end end
のように書きなおしてみると…
$ rspec . Finished in 0.03378 seconds 1 example, 0 failures
うまくいった!
まとめ
というわけで、Sinatraの動きをもう少しちゃんと調べましょうというお話でした。