157
155

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Rails] RSpecのリファクタリング

Last updated at Posted at 2013-11-25

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のリファクタリング

指定のページが指定の要素を持っている(もしくはいない)かをチェックするテストコード。

spec/requests/static_pages_spec.rb
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の機能のひとつ。

これらを用いてリファクタリングしたテスト。

spec/requests/static_pages_spec.rb
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を用いる

app/helpers/application_helper.rb
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を用いて、もう一段簡潔になった。

spec/requests/static_pages_spec.rb
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に対するテストも忘れずに。

spec/helpers/application_helper_spec.rb
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("")) }

が実行される。

だいぶ簡潔になった

spec/requests/static_pages_spec.rb
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

157
155
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
157
155

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?