Ruby on Rails Tutorialのエッセンスを自分なりに整理してみる7
Railsにおけるリンクの記述方法とそのテスト
http://qiita.com/kidachi_/items/d704e7eb63513c3831ae
の続き。
Ruby on Rails Tutorial(chapter5)
http://railstutorial.jp/chapters/filling-in-the-layout#sec-layout_exercises
##Rspecのリファクタリング
指定のページが指定の要素を持っている(もしくはいない)かをチェックするテストコード。
require 'spec_helper'
describe "Static pages" do
describe "Home page" do
it "should have the h1 'Sample App'" do
visit root_path
page.should have_selector('h1', text: 'Sample App')
end
it "should have the base title" do
visit root_path
page.should have_selector('title',
text: "Ruby on Rails Tutorial Sample App")
end
it "should not have a custom page title" do
visit root_path
page.should_not have_selector('title', text: '| Home')
end
end
describe "About page" do
it "should have the h1 'About'" do
visit about_path
page.should have_selector('h1', text: 'About Us')
end
it "should have the title 'About Us'" do
visit about_path
page.should have_selector('title',
text: "Ruby on Rails Tutorial Sample App | About Us")
end
end
~
end
まず内容理解から。
describe
テストの塊を表す。例えば
describe "Static pages" do
describe "Home page" do
end
end
ならば、「Static pages関連のテストの中のひとつである『Home page』のテスト」。
it "hoge" do ~ end
「hoge」はあくまでラベル。プログラム的な意味はない。
テスト実行時、失敗箇所を示すものとして表示される。
ブロック(do~end)の中身が実際の各テストのロジックとなる。
it "should have the h1 'Sample App'" do
visit root_path
page.should have_selector('h1', text: 'Sample App')
end
上の例では、「root_path(/)にアクセスした際、そのページが
'Sample App'という文字列のh1タグを持っていること」となる。
洗練1
visit~、expect(page).to ~など、似たような構文が重複している。
→beforeとsubjectを用いて解決。
before {}
各テストが走る前に毎回実行されるメソッド。
→重複部を before に書くことでテストコードの重複を排除出来る。
###subject {}
ブロック({})内の評価結果が it 内のshouldのレシーバとなる。
本例テストのレシーバ(主語)はどちらもpageになるので、subjectとして括ることが出来る。
※page
レスポンスがHTMLである場合、その内容を本メソッドで取得できる。
これはCapybaraの機能のひとつ。
これらを用いてリファクタリングしたテスト。
require 'spec_helper'
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_selector('h1', text: 'Sample App') }
it { should have_selector 'title',
text: "Ruby on Rails Tutorial Sample App" }
it { should_not have_selector 'title', text: '| Home' }
end
describe "About page" do
before { visit root_path }
it { should have_selector('h1', text: 'About) }
it { should have_selector 'title',
text: "Ruby on Rails Tutorial Sample App | About Us" }
end
洗練2
text: "Ruby on Rails Tutorial Sample App"の部分をまとめるため、
helperを用いる
module ApplicationHelper
# ページごとの完全なタイトルを返却
def full_title(page_title)
base_title = "Ruby on Rails Tutorial Sample App"
if page_title.empty?
base_title
else
"#{base_title} | #{page_title}"
end
end
end
helperメソッドfull_titleを用いて、もう一段簡潔になった。
require 'spec_helper'
subject { page }
describe "Home page" do
before { visit root_path }
it { should have_selector('h1', text: 'Sample App') }
it { should have_selector('title', text: full_title('')) }
it { should_not have_selector 'title', text: '| Home' }
end
describe "About page" do
before { visit about_path }
it { should have_selector('h1', text: 'About') }
it { should have_selector('title', text: full_title('About Us')) }
end
helperに対するテストも忘れずに。
require 'spec_helper'
describe ApplicationHelper do
describe "full_title" do
it "should include the page title" do
expect(full_title("foo")).to match(/foo/)
end
it "should include the base title" do
expect(full_title("foo")).to match(/^Ruby on Rails Tutorial Sample App/)
end
it "should not include a bar for the home page" do
expect(full_title("")).not_to match(/\|/)
end
end
end
洗練3
it { should have_selector('h1', text: 'xxx') }
が各describeで重複している。
→RSpecのShared Exampleとlet(変数生成)を使用してテストの冗長性を排除する。
Shared Exampleとは
shared_examples_for "fuga" do ~ end
で、各describeに共通するテストを記述できる。
その呼び出しは
it_should_behave_like "fuga"
で行う。
letとは
テスト内にローカル変数を作成する。
シンボルとブロックを引数に取り、ブロックが返した値を保持するローカル変数となる。
変数の名前はシンボルの値。
let(:variable) { 'hoge' }
# 値’hoge’を持つ変数variableが使えるようになる。
生成された変数はテスト中すべてのbeforeまたはitブロックで利用できる。
共通箇所の定義
Shared Exampleで共通箇所を定義する。
またローカル変数variable1,2を用いる(実際の定義は各describeで行う)。
describe "Static pages" do
subject { page }
shared_examples_for "fuga" do
it { should have_content(variable1) }
it { should have_title(full_title(variable2)) }
end
~
その上で各describeで呼び出し。
- ローカル変数variableへの値代入(let(:variable) { value })
- Shared Exampleの呼び出しはit_should_behave_like "fuga"
describe "Home page" do
let(:variable1) { 'Sample App' }
let(:variable2) { '' }
it_should_behave_like "fuga"
end
→describe "Home page" 内で、
- it { should have_content('Sample App') }
- it { should have_title(full_title("")) }
が実行される。
だいぶ簡潔になった
require 'spec_helper'
describe "Static pages" do
subject { page }
shared_examples_for "all static pages" do
it { should have_content(heading) }
it { should have_title(full_title(page_title)) }
end
describe "Home page" do
before { visit root_path }
let(:heading) { 'Sample App' }
let(:page_title) { '' }
it_should_behave_like "all static pages"
it { should_not have_title('| Home') }
end
describe "About page" do
before { visit about_path }
let(:heading) { 'About' }
let(:page_title) { 'About' }
it_should_behave_like "all static pages"
end
以下に続く
[Rails] 基本的なユーザ管理用Modelをテスト駆動で実装してみる
http://qiita.com/kidachi_/items/99f2c90788bd931ea3ee