RSpecを使ってSpecを書いていると何度も同じことを書くことが多いです。
たとえばパラメータに応じてページタイトルを変えたい場合とか、validationがきちんと行われているかとか。。。
毎回、 it “should have hoge” do … end と書いているのは大変です。
RSpecではいくつかの方法を使用して、もっと簡単に書くことができます。
クラスメソッドを使う
一つのspecファイルで何度も使うものであればクラスメソッドにします。
[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]
describe "/articles" do def self.it_should_have_tag(selector, content) it "should have '#{content}' at '#{selector}'" do response.should(have_tag(selector, content)) end end end
と書いておけば、以下のような書き方が可能になります。
[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]
describe "/articles" do def self.it_should_have_tag(selector, content) it "should have '#{content}' at '#{selector}'" do response.should(have_tag(selector, content)) end end it_should_have_tag('tr>td', 'MyString') end
it_should_have_tag(‘tr>td’, ‘MyString’) の部分だけで適切なspecに展開されます。
1行で簡潔に書けますので、理解する時間も短くてすみます。
自分はページタイトル、meta description、meta keywords、RSSのスペックに以下のコードを使用しています。
[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]
describe "/articles" do def self.it_should_have_tag(selector, content) it "should have '#{content}' at '#{selector}'" do response.should(have_tag(selector, content)) end end def self.it_should_have_page_title(title) it_should_have_tag('head>title', title) end def self.it_should_have_meta_description(meta_description) it_should_have_tag('head>meta[name=description][content=?]', meta_description) end def self.it_should_have_meta_keywords(meta_keywords) it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords) end def self.it_should_have_rss_link(link) it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link) end end
モジュールに追い出す
さらによく使う項目、上記のhtmlに関するspecなどはたくさんのところに書かれるであろうことが推測されます。
そういう場合には、モジュールに追い出してしまうということが可能だということを同僚のquekさんに教えていただきました。1
$RAILS_ROOT/spec/support フォルダにモジュールのファイルを書きます。
上記の例であれば
[$RAILS_ROOT/spec/support/tag_macros.rb]
module TagMacros def self.included(base) base.extend(ClassMethods) end module ClassMethods def it_should_have_tag(selector, content) it "should have '#{content}' at '#{selector}'" do response.should(have_tag(selector, content)) end end def it_should_have_page_title(title) it_should_have_tag('head>title', title) end def it_should_have_meta_description(meta_description) it_should_have_tag('head>meta[name=description][content=?]', meta_description) end def it_should_have_meta_keywords(meta_keywords) it_should_have_tag('head>meta[name=keywords][content=?]', meta_keywords) end def it_should_have_rss_link(link) it_should_have_tag('head>link[rel=alternate][type=application/rss+xml][href=?]', link) end end end
使用する際には include TagMacros を書いておきます。
[$RAILS_ROOT/spec/views/articles/index.html.erb_spec.rb]
describe "/articles" do include TagMacros it_should_have_tag('tr>td', 'MyString') end
自分のような include 〜 を書くのもものぐさだという人は、 $RAILS_ROOT/spec/spec_helper.rb に config.include メソッドを使用すれば、自動的に読み込まれるようになります。
[$RAILS_ROOT/spec/spec_helper.rb]
Spec::Runner.configure do |config| # ... config.include(TagMacros, :type => [:views]) # ... end
上記のように :type を指定すれば view だけで読み込まれるというような指定も可能です。
参考
Railscasts – RSpec Matchers & Macros
Writing Macros in RSpec • Blog Archive • Ben Mabey
P.S.
参考にしたサイトではMacroと書いてありますが、どうもこれをMacroと読んでいいものなのかどうかかなり謎なので、タイトルはMacro?と?付きにしてみましたwww
- quekさんによると、下の「参考」のところに書いた Railscasts に同様のことが書いてあり、そのアドレスを過去の自分は社内のチケットに書いていたらしいのですが、モジュールに出すことは全く知りませんでしたwww [↩]