仕事で初めてまともにSinatra使って少しハマったのでメモ。
Sinatraでアプリ書く場合にはClassic StyleとModuler Applicationていう2種類がある。よく見るサンプルなんかは大抵Classic Styleで書かれているものが多いかと。
Sinatra::Base - Middleware, Libraries, and Modular Apps
今回、Moduler Applicationていうタイプでコードを書いてたんだけどテスト書いててハマった。
というのも Testing Sinatra with Rack::Test を見ながら書いてたんだけど、どうにもテストがうまく動かない。
上記ページに載ってるサンプルのうちRack::Testとrspecを使った場合は次のような感じ。
以下のサンプルアプリに対して、
require 'sinatra' get '/' do "Hello World #{params[:name]}".strip end
テストコードのサンプルはこんな感じ。
ENV['RACK_ENV'] = 'test' require './hello_world' require 'rspec' require 'rack/test' describe 'The HelloWorld App' do include Rack::Test::Methods def app Sinatra::Application end it "says hello" do get '/' expect(last_response).to be_ok expect(last_response.body).to eq('Hello World') end end
このテストはもちろんそのまま動く。
問題はこのサンプルはClassic Styleで書かれているってことで、自分のコードではModule Applicationとして書いていた。
例えば、同内容をModuler Applicationとして書き直すとこんな感じになる。
require 'sinatra/base' class App < Sinatra::Base get '/' do "Hello World #{params[:name]}".strip end end
大きな違いはrequireしているのがsinatra/baseになる。
そしてこの状態になると先ほどのテストコードは動かなくなる。
何も修正せずに上記のテストを実行するとこんな感じになるはず。
The HelloWorld App says hello (FAILED - 1) Failures: 1) The HelloWorld App says hello Failure/Error: expect(last_response).to be_ok expected ok? to return true, got false # ./hello_world_spec.rb:16:in `block (2 levels) in <top (required)>' Finished in 0.03446 seconds 1 example, 1 failure Failed examples: rspec ./hello_world_spec.rb:14 # The HelloWorld App says hello
結論から言ってしまうとModulerスタイルで書いた場合はRack::Test使うときに必要になるappというメソッド内で定義しているSinatra::Applicationを自分のアプリのクラス名にする必要があるってこと。
つまりこうする必要がある。
ENV['RACK_ENV'] = 'test' require './hello_world' require 'rspec' require 'rack/test' describe 'The HelloWorld App' do include Rack::Test::Methods def app HelloWorld #=> Sinatra::Application ではなく自分のアプリのクラス名 end it "says hello" do get '/' expect(last_response).to be_ok expect(last_response.body).to eq('Hello World') end end
なんでこうなるかを簡単に言うと、Classic Styleで require 'sinatra' すると実はsinnatra.rb内では上述のsinatra/baseが呼ばれていて、実体としてはSinatra::ApplicationというModuler Applicationが定義されてる。
つまり、Classic StyleとはModuler Application + α だということ。ここのαの部分にbuilt-in serverなんかが定義されてたり。
というわけでModuler Applicationの場合はSinatra::Applicationではなく自分のクラスを使いましょうということ。
ちなみにModuler Applicationの場合はbuilt-in serverがないのでconfig.ruを用意してrackupしたりするよね?
そのときに指定するクラスは自分のクラスになるわけで、つまりはそういうこと。
require './hello_world' run HelloWorld
知ってる人からすりゃ当たり前な少し恥ずかしい話でした。