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さんに教えていただきました。
$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