Ruby

Rails Tutorialsで勉強のメモ その3(静的なページ作成)

More than 3 years have passed since last update.


やること


  • 静的ページを作成


  • Capybaraを使って結合テスト


準備


  • Railsプロジェクトの作成

$ cd rails_projects/

# --skip-test-unitでtestディレクトリを作成しない
# 別途rspecでテストを記載する
$ rails new sample_app --skip-test-unit

$ cd sample_app/

$ vim Gemfile


  • Gemfileの編集

source 'https://rubygems.org'

ruby '2.0.0'

gem 'rails', '4.0.4'

group :development, :test do
gem 'sqlite3', '1.3.8'
gem 'rspec-rails', '2.13.1'
end

group :test do
gem 'selenium-webdriver', '2.35.1'
gem 'capybara', '2.1.0'
end

gem 'sass-rails', '4.0.2'
gem 'uglifier', '2.1.1'
gem 'coffee-rails', '4.0.1'
gem 'jquery-rails', '3.0.4'
gem 'turbolinks', '1.1.1'
gem 'jbuilder', '1.0.2'

group :doc do
gem 'sdoc', '0.3.20', require: false
end

# Heroku用の設定
group :production do
gem 'pg', '0.15.1'
gem 'rails_12factor', '0.0.2'
end



  • gemのインストール

$ bundle install --without production

$ bundle update
$ bundle install


  • セッション変数の暗号化に使用するための、秘密トークンの設定



    • .gitignore.secretをリポジトリで公開しないようにする




config/initializers/secret_token.rb

require 'securerandom'

def secure_token
token_file = Rails.root.join('.secret')
if File.exist?(token_file)
# 既に.secretファイルが存在している場合はそのまま使用
File.read(token_file).chomp
else
# 動的にトークンを生成
token = SecureRandom.hex(64)
File.write(token_file, token)
token
end
end

SampleApp::Application.config.secret_key_base = secure_token



  • テストで使用するツールの変更



    • Test::Unitの代わりにRspecを使用



# .rspecファイルとspecディレクトリ、spec/spec_helper.rbが作成される

$ rails generate rspec:install


  • リポジトリの初期化

$ git init

$ git add .
$ git commit -m "Initial commit"


  • READMEの変更


    • 変更内容は割愛



  • READMEファイルをMarkdown形式に変更してコミット

$ git mv README.{rdoc,md}

$ git commit -am "Improve the README"


  • サンプルアプリケーションをGitHubにpush


    • 事前にGitHub上にリポジトリ作成しておく



$ git remote add origin https://asam-3@github.com/ASAM-3/sample_app.git

$ git push -u origin master


  • Herokuにデプロイ

$ heroku create

$ git push heroku master
$ heroku run rake db:migrate


  • Herokuのデプロイに失敗した時の対応


    • ログの確認

    • プリコンパイルしてからpush



# ログの確認

$ heroku logs

# プリコンパイルしてからpush
$ rake assets:precompile
$ git add .
$ git commit -m "Add precompiled assets for Heroku"
$ git push heroku master


  • 定期的なGitHubへのpushやHerokuへのデプロイをすべき

$ git push

$ git push heroku
$ heroku run rake db:migrate


静的ページ


  • トピックブランチの作成

$ git checkout -b static-pages


  • StaticPagesコントローラの作成



    • homehelpというアクションを作成

    • 今回はrspecのテストをしないため、--no-test-frameworkオプションをつける



$ rails generate controller StaticPages home help --no-test-framework

create app/controllers/static_pages_controller.rb
route get "static_pages/help"
route get "static_pages/home"
invoke erb
create app/views/static_pages
create app/views/static_pages/home.html.erb
create app/views/static_pages/help.html.erb
invoke helper
create app/helpers/static_pages_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/static_pages.js.coffee
invoke scss
create app/assets/stylesheets/static_pages.css.scss

# なお、下記コマンドでもstatic_pages_controller.rbを作成できる
# キャメル記法→スネークケースに変換される
$ rails generate controller static_pages

# 自動生成を取り消ししたい場合はrails destroyを使う
$ rails destroy controller StaticPages


  • 自動生成されたRailsルータの確認



    • homeアクションとhelpアクションのルーティングが記載されている


    • getはGETリクエストを表す


    • /static_pages/home/static_pages/helpでそれぞれアクセスできる




config/routes.rb

SampleApp::Application.routes.draw do

get "static_pages/home"
get "static_pages/help"
end



  • 自動生成されたコントローラ


    • 生成時に指定したhomeアクションとhelpアクションが生成されている


    • RESTではない

    • 静的なページを扱う場合には向いている




  • REST(REpresentational State Transfer)


    • インターネットやWebアプリケーションなどの、分散/ネットワーク化されたシステムやアプリケーションを構築するためのアーキテクチャの一つ

    • Railsアプリにおいては、アプリケーションを構成するコンポーネントをリソースとしてモデル化すること


      • その2で言うところのusermicropost






  • RESTfulなスタイルを採用することで得られるメリット


    • 作成すべきコントローラやアクションの決定が容易になる




app/controllers/static_pages_controller.rb

class StaticPagesController < ApplicationController

def home
end

def help
end
end




  • homeメソッドとhelpメソッド


    • 実装が空だが、Railsの場合はApplicationControllerクラスを継承しているので、Rails特有の振る舞いをする


    • /static_pages/homeにアクセスした場合の振る舞い



      1. homeメソッドに記述されているコードを実行

      2. アクションに対応するビューを出力


        • /app/views/static_pages/home.html.erb







  • 自動生成されたビュー



app/views/static_pages/home.html.erb

<h1>StaticPages#help</h1>

<p>Find me in app/views/static_pages/help.html.erb</p>


  • ここまででコミット

$ git add .

$ git commit -m "Add a StaticPages controller"


テスト



  • 振舞い駆動開発(Behavior-Driven Development, BDD)


    • アプリケーションの振舞いをテストしながら実装




  • 結合テスト


    • ユーザがアプリケーションを使う際の一連のアクションをシミュレーション

    • アプリケーションの各ページが正常に動作するかテスト

    • 結合テストのためのツール


      • Capybara

      • Cucumber






  • テスト駆動開発(TDD)


    • アプリケーションを開発する際に最初にテストを作成し、次にコードを作成




テスト駆動開発


  • テスト駆動開発の用語



    • Red:失敗するテスト


    • Green:テストにパスするコード

    • リファクタリング:動作を変えずにコードを改善、冗長なコードの削除




  • 上記の順に開発を行っていく


    • このサイクルのことをRed/Green/Refactorという



  • 結合テストの生成


$ rails generate integration_test static_pages

invoke rspec
create spec/requests/static_pages_spec.rb


  • テストコードの記述



    • describe


      • テストの説明を引数として渡す




    • visit



      • Capybaravisit機能を使い、ブラウザでの/static_pages/homeへのアクセスをシミュレート




    • expect



      • Capybaraが提供するpage変数を使用


      • have_contentの引数がコンテンツに含まれているかをテスト






spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do
describe "Home page" do
it "should have the content 'Sample App'" do
visit '/static_pages/home'
expect(page).to have_content('Sample App')
end
end
end




  • spec/spec_helper.rbの変更



    • Capybara DSLRSpecヘルパーファイルに追加




spec/spec_helper.rb

RSpec.configure do |config|

config.include Capybara::DSL
end


  • テストの実行


    • 現時点ではエラーになる



$ bundle exec rspec spec/requests/static_pages_spec.rb

F

Failures:

1) StaticPages Home page should have the content 'Sample App'
Failure/Error: expect(page).to have_content('Sample App')
expected #has_content?("Sample App") to return true, got false
# ./spec/requests/static_pages_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.68511 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/requests/static_pages_spec.rb:5 # StaticPages Home page should have the content 'Sample App'

Randomized with seed 27019



  • home.html.erbを修正


    • 本文の中にSample Appという文言を追加




app/views/static_pages/home.html.erb

<h1>Sample App</h1>

<p>
This is the home page for the
<a href="http:/
/railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>


  • テストを再実行


    • 今度は成功



$ bundle exec rspec spec/requests/static_pages_spec.rb

.

Finished in 0.10047 seconds
1 example, 0 failures

Randomized with seed 39422


  • Helpページのテストコードを追加


spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do
describe "Home page" do
it "should have the content 'Sample App'" do
visit '/static_pages/home'
expect(page).to have_content('Sample App')
end
end

# ここから追加
describe "Help page" do
it "should have the content 'Help'" do
visit '/static_pages/help'
expect(page).to have_content('Help')
end
end
# ここまで追加
end



  • テストを実行



    • Help部分がエラー



$ bundle exec rspec spec/requests/static_pag

es_spec.rb
F.



  • help.html.erbを修正


    • 本文にHelpを追加




app/views/static_pages/help.html.erb

<h1>Help</h1>

<p>
Get help on the Ruby on Rails Tutorial at the
<a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
To get help on this sample app. see the
<a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
</p>


  • 再度テストを実施


    • 今度は成功



$ bundle exec rspec spec/requests/static_pages_spec.rb

..


ページの追加

* Aboutページを作成

* まずはテストコードを記載

* そしてテスト実行

* aboutへのルートが設定されていないためエラー


spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do
describe "Home page" do
it "should have the content 'Sample App'" do
visit '/static_pages/home'
expect(page).to have_content('Sample App')
end
end

describe "Help page" do
it "should have the content 'Help'" do
visit '/static_pages/help'
expect(page).to have_content('Help')
end
end

# ここから追加
describe "About page" do
it "should have the content 'About Us'" do
visit '/static_pages/about'
expect(page).to have_content('About Us')
end
end
# ここまで追加
end



テスト実行(ルートがないためエラー)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
F..

Failures:

1) StaticPages About page should have the content 'About Us'
Failure/Error: visit '/static_pages/about'
ActionController::RoutingError:
No route matches [GET] "/static_pages/about"
# ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'




  • routesファイルに/static_pages/aboutへのルートを追加してテスト



    • StaticPagesControlleraboutアクションがないためエラー




config/routes.rb

SampleApp::Application.routes.draw do

get "static_pages/home"
get "static_pages/help"
## ここから追加
get "static_pages/about"
## ここまで追加
end


テスト実行(aboutアクションが存在しないためエラー)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
..F

Failures:

1) StaticPages About page should have the content 'About Us'
Failure/Error: visit '/static_pages/about'
AbstractController::ActionNotFound:
The action 'about' could not be found for StaticPagesController
# ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'




  • StaticPagesControlleraboutアクションを追加


    • ビューなどのテンプレートが見つからない




app/controllers/static_pages_controller.rb

class StaticPagesController < ApplicationController

def home
end

def help
end

# ここから追加
def about
end
# ここまで追加
end



テスト実行(ビューなどのテンプレートが見つからないためエラー)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
F..

Failures:

1) StaticPages About page should have the content 'About Us'
Failure/Error: visit '/static_pages/about'
ActionView::MissingTemplate:
Missing template static_pages/about, application/about with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
* "/home/asam-3/rails_projects/sample_app/app/views"
# ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'




  • aboutビューを追加


    • テスト成功




app/views/static_pages/about.html.erb

<h1>About Us</h1>

<p>
The <a href="http:/
/railstutorial.org/">Ruby on Rails Tutorial</a>
is a project to make a book and screencasts to teach web development
with <a href="
http://rubyonrails.org/">Ruby on Rails</a>. This
is the sample application for the tutorial.
</p>


テスト実施(成功)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
...

Finished in 0.13243 seconds
3 examples, 0 failures



  • リファクタリング


    • この時点では特になし




新規ページ追加時の手順


  1. 新規ページのテストコード追加


  2. config/routesにルーティングを追加

  3. コントローラにアクションを追加

  4. ビューを追加


動的なページ


  • タイトル変更のテスト


    • ページ毎に異なるタイトルが表示されるように変更




  • 作業手順


    1. タイトルのテストを作成

    2. タイトルを追加

    3. レイアウトファイルを使ったリファクタリングと重複の削除を実施




  • レイアウトファイルのリネーム


    • 後でレイアウトファイルの役割を確認するため、一旦無効にする




レイアウトファイルのりネーム

$ mv app/views/layouts/application.html.erb{,_bk}



  • タイトルは下記のように設定

URL
タイトル

/static_pages/home
Ruby on Rails Tutorial Sample App - Home

/static_pages/help
Ruby on Rails Tutorial Sample App - Help

/static_pages/about
Ruby on Rails Tutorial Sample App - About Us


  • テストコードの記載とテスト実行


    • もちろんエラーになる




spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do
describe "Home page" do
it "should have the content 'Sample App'" do
visit '/static_pages/home'
expect(page).to have_content('Sample App')
end

# ここから追加
it "should have the title 'Home'" do
visit '/static_pages/home'
expect(page).to have_title("Home")
end
# ここまで追加
end

describe "Help page" do
it "should have the content 'Help'" do
visit '/static_pages/help'
expect(page).to have_content('Help')
end

# ここから追加
it "should have the title 'Help'" do
visit '/static_pages/help'
expect(page).to have_title("Help")
end
# ここまで追加
end

describe "About page" do
it "should have the content 'About Us'" do
visit '/static_pages/about'
expect(page).to have_content('About Us')
end

# ここから追加
it "should have the title 'About'" do
visit '/static_pages/about'
expect(page).to have_title("About")
end
# ここまで追加
end
end



テスト実施(タイトル部分がエラー)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
.F.F.F


  • 各ビューにタイトルを追加


    • テスト成功

    • ただ、同じようなコードが繰り返されているため、保守性が低い




app/views/static_pages/home.html.erb

<!DOCTYPE html>

<html>
<head>
<title>Ruby on Rails Tutorial Sample App - Home</title>
</head>
<body>
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>
</body>
</html>


app/views/static_pages/help.html.erb

<!DOCTYPE html>

<html>
<head>
<title>Ruby on Rails Tutorial Sample App - Help</title>
</head>
<body>
<h1>Help</h1>
<p>
Get help on the Ruby on Rails Tutorial at the
<a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
To get help on this sample app. see the
<a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
</p>
</body>
</html>


app/views/static_pages/about.html.erb

<!DOCTYPE html>

<html>
<head>
<title>Ruby on Rails Tutorial Sample App - About Us</title>
</head>
<body>
<h1>About Us</h1>
<p>
The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
is a project to make a book and screencasts to teach web development
with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This
is the sample application for the tutorial.
</p>
</body>
</html>


テスト実施(成功)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
......

Finished in 0.08417 seconds
6 examples, 0 failures



  • リファクタリング


    • ページのタイトルがほぼ同じ


    • Ruby on Rails Tutorial Sample Appが3つのタイトルで共通している

    • HTMLの構造全体が各ページで重複している



  • DRY(Don't Repeat Yourself)に反している



  • 埋め込みRubyの利用


    • Railsのprovide関数を使用して、タイトルをページ毎に変更


    • ERbはWebページに動的な要素を加えるときに使うテンプレートシステム




  • まずはhome.html.erbだけ変更


    • 変更後にテストを実施して成功することを確認




app/views/static_pages/home.html.erb

<% provide(:title, 'Home') %>

<!DOCTYPE html>
<html>
<head>
<title>Ruby on Rails Tutorial Sample App - <%= yield(:title) %></title>
</head>
<body>
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>
</body>
</html>



  • provide関数



    • :titleというラベルに'Home'という文字列を関連付ける




  • yield関数


    • テンプレートにタイトルを挿入




テスト実行(成功)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
......

Finished in 0.08411 seconds
6 examples, 0 failures




  • helpaboutも同じような感じで記述できる

  • 各ERbファイルの構成は下記のとおり



    • htmlheadの構成は全て共通


    • bodyの中身だけ異なる



<% provide(:title, 'タイトル') %>

<!DOCTYPE html>
<html>
<head>
<title>Ruby on Rails Tutorial Sample App - <%= yield(:title) %></title>
</head>
<body>
中身
</body>
</html>


  • レイアウトファイルの変更


    • 元のファイルからタイトル部分だけ変更




レイアウトファイルのリネーム

$ mv app/views/layouts/application.html.erb{_bk,}




  • <% yield %>


    • 各ページの内容をレイアウトに挿入


    • /static_pages/homeにアクセスすると、home.html.erbの内容がHTMLに変換され、<%= yield %>の位置に挿入される




  • <%= stylesheet_link_tag ... %>


    • スタイルシートをインクルード


    • Asset Pipelineの一部




  • <%= javascript_include_tag "application", ... %>


    • JavaScriptをインクルード


    • Asset Pipelineの一部




  • <%= csrf_meta_tags %>


    • クロスサイトリクエストフォージェリを防ぐために使用するRailsのメソッド




app/views/layouts/application.html.erb

<!DOCTYPE html>

<html>
<head>
<title>Ruby on Rails Tutorial Sample App - <%= yield(:title) %></title>
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>



  • 各種ビューをレイアウトを利用した形に変更

  • リファクタリング後にテスト実施


app/views/static_pages/home.html.erb

<% provide(:title, 'Home') %>

<h1>Sample App</h1>
<p>
This is the home page for the
<a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
sample application.
</p>


app/views/static_pages/help.html.erb

<% provide(:title, 'Help') %>

<h1>Help</h1>
<p>
Get help on the Ruby on Rails Tutorial at the
<a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
To get help on this sample app. see the
<a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
</p>


app/views/static_pages/about.html.erb

<% provide(:title, 'About Us') %>

<h1>About Us</h1>
<p>
The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
is a project to make a book and screencasts to teach web development
with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This
is the sample application for the tutorial.
</p>


テスト実行(成功)

$ bundle exec rspec spec/requests/static_pag

es_spec.rb
......

Finished in 0.16055 seconds
6 examples, 0 failures



コミットとpush


  • コミット


  • masterブランチとマージ

  • GitHubにアップロード

  • herokuにアップロード

$ git add .

$ git commit -m "Finish static pages"
$ git checkout master
$ git merge static-pages
$ git push
$ git push heroku


演習


Contactページを作成


  1. テストコード作成


  2. config/routes.rb'にcontact`のルーティングを追加


  3. static_pages_controller.rbcontactアクションを追加


  4. contact.html.erbを追加


spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do
describe "Contact page" do
it "should have the content 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_content('Contact')
end

it "should have the title 'Contact'" do
visit '/static_pages/contact'
expect(page).to have_title("Contact")
end
end
end



config/routes.rb

SampleApp::Application.routes.draw do

get "static_pages/home"
get "static_pages/help"
get "static_pages/about"
get "static_pages/contact"
end


app/controllers/static_pages_controller.rb

class StaticPagesController < ApplicationController

def home
end

def help
end

def about
end

def contact
end
end



app/views/static_pages/contact.html.erb

<% provide(:title, 'Contact') %>

<h1>Contact</h1>
<p>
Contact Ruby on Rails Tutorial about the sample app at the
<a href="http:/
/railstutorial.jp/contact">contact page</a>.
</p>


specテストの重複部分をletを使ってまとめる


  1. タイトルの文字列全体をテストするように変更



    • letで共通部分を定義

    • テスト部分で文字列連結してテスト




spec/requests/static_pages_spec.rb

require 'spec_helper'

describe "StaticPages" do

let(:base_title) { "Ruby on Rails Tutorial Sample App" }

describe "Home page" do
it "should have the content 'Sample App'" do
visit '/static_pages/home'
expect(page).to have_content('Sample App')
end

it "should have the title 'Home'" do
visit '/static_pages/home'
expect(page).to have_title("#{base_title} - Home")
end
end
# 中略
end



その他のセットアップ


bundle execを使わない



  • bundle exec


    • 現在の環境に依存するgemをGemfileから読み込んでプログラムを実行するために指定が必要


    • RVMを使用している場合、RVMを最新化することにより、インストールされたgemが自動的に適切なBundlerの環境で実行される(1.11.x以降)




  • rakerspecを実行する際に指定していたが、今後は使わない


rvmの最新化

$ rvm get stable

$ rvm -v


rspecの実行

$ rspec spec/



Guardによるテストの自動化



  • Guard


    • テストを自動化するgem

    • ファイルシステムの変更を監視し、ファイルが変更されると自動実行するようにできる


      • specファイル:*_spec.rb

      • viewファイル:*.html.erb など






  • Guardのセットアップ



    1. Gemfileguard-rspecを追加

    2. gemのインストール


    3. Guardの初期化


    4. Guardfileの編集




Gemfile

# 変更箇所の抜粋

group :development, :test do
gem 'sqlite3', '1.3.8'
gem 'rspec-rails', '2.13.1'
# ここから追加
gem 'guard-rspec', '2.5.0'
# ここまで追加
end


gemのインストール、Guardの初期化

$ bundle install

# Guardfileが生成される
$ bundle exec guard init rspec




  • guard 'rspec', all_after_pass: false do


    • 失敗したテストがある場合は以降のテストを実施しない




Guardfile(追加・変更点のみ)

require 'active_support/inflector'

guard 'rspec', all_after_pass: false do

# 中略

# Custom Rails Tutorial specs
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m|
["spec/routing/#{m[1]}_routing_spec.rb",
"spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
"spec/acceptance/#{m[1]}_spec.rb",
(m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
"spec/requests/#{m[1].singularize}_pages_spec.rb")]
end

watch(%r{^app/views/(.+)/}) do |m|
(m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
"spec/requests/#{m[1].singularize}_pages_spec.rb")
end

watch(%r{^app/controllers/sessions_controller\.rb$}) do |m|
"spec/requests/authentication_pages_spec.rb"
end
end



Sporkを使ったテストの高速化



  • rspecはテスト開始までに時間がかかってしまう



    • rspecを実行するたびにRailsの環境全体を読み込みなおす必要があるため




  • Sporkテストサーバ


    • 環境を1回だけ読み込み、今後実行するテストのためのプロセスを管理する




  • Sporkのセットアップ手順


    1. GemfileにSporkを追加


    2. gemのインストール


    3. Sporkの設定にbootstrapを指定


    4. RSpecの設定を変更


      • 環境の読み込みを1回で済ますため、spec/spec_helper.rbの中で環境をpreforkのブロックで読み込むようにする



    5. テストスイートを実行し、基準となる実行時間を測定


    6. Sporkサーバを起動


    7. .rspecの設定を変更




Gemfile(変更箇所のみ抜粋)

group :development, :test do

gem 'sqlite3', '1.3.8'
gem 'rspec-rails', '2.13.1'
gem 'guard-rspec', '2.5.0'
gem 'spork-rails', '4.0.0'
gem 'guard-spork', '1.5.0'
gem 'childprocess', '0.3.6'
end


gemのインストール

$ bundle install

$ bundle update


Sporkの設定にbootstrapを指定

$ bundle exec spork --bootstrap



spec/spec_helper.rb(環境をpreforkのブロックで読み込む)

require 'rubygems'

require 'spork'

Spork.prefork do
# 既存のコードをカット&ペースト
ENV["RAILS_ENV"] ||= 'test'

# 中略

config.include Capybara::DSL
end
end

Spork.each_run do
# This code will be run each time you run your specs.

end



rspecの実行時間を計測

$ time bundle exec rspec spec/requests/static_pages_spec.rb

........

Finished in 0.19846 seconds
8 examples, 0 failures

Randomized with seed 25711

real 0m4.395s # テストスイートの時間
user 0m3.818s
sys 0m0.517s # テストの実行時間



別ターミナルでSporkサーバを起動

$ cd rails_projects/sample_app/

$ bundle exec spork
Using RSpec, Rails
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!



--drbオプションをつけてrspecの実行時間を計測

$ time bundle exec rspec spec/requests/static_pages_spec.rb --drb

........

Finished in 0.32188 seconds
8 examples, 0 failures

Randomized with seed 8842

real 0m1.633s
user 0m1.037s
sys 0m0.104s



--drbオプションを明示的に指定しないように、.rspecに設定を追加

$ sed -i -e '/--drb/d' .rspec

$ echo "--drb" >> .rspec

# --drbなしでも高速で終了
$ time bundle exec rspec spec/requests/static_pages_spec.rb
........

Finished in 0.20508 seconds
8 examples, 0 failures

Randomized with seed 8842

real 0m1.218s
user 0m0.786s
sys 0m0.085s



GuardにSporkを導入


SporkとGuardを併用

$ bundle exec guard init spork

16:23:28 - INFO - spork guard added to Guardfile, feel free to edit it



  • bundle exec guard init sporkを実行すると、Guardfileに設定が追加される


  • guard--drbの設定を追加



    • rspecが高速化




Guardfile

# 自動生成された部分

guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
watch('config/application.rb')
watch('config/environment.rb')
watch('config/environments/test.rb')
watch(%r{^config/initializers/.+\.rb$})
watch('Gemfile')
watch('Gemfile.lock')
watch('spec/spec_helper.rb') { :rspec }
watch('test/test_helper.rb') { :test_unit }
watch(%r{features/support/}) { :cucumber }
end

# cli: '--drb'を追加
guard 'rspec', all_after_pass: false, cli: '--drb' do
...
end



GuardとSporkを同時に起動

$ bundle exec guard