その1が長くなってしまったので、つづきです。
Spork を使う
テスト実行時の時間を短縮するため、Spork を導入します。
まず、gem をインストールします。
$ vim Gemfile
group :development, :test do
gem 'rspec-rails', '2.10.1'
gem 'spork', '0.9.2'
end
$ bundle
Fetching source index for https://rubygems.org/
Using rake (0.9.2.2)
Using i18n (0.6.0)
Using multi_json (1.3.6)
Using activesupport (3.2.6)
Using builder (3.0.0)
Using activemodel (3.2.6)
Using erubis (2.7.0)
Using journey (1.0.4)
Using rack (1.4.1)
Using rack-cache (1.2)
Using rack-test (0.6.1)
Using hike (1.2.1)
Using tilt (1.3.3)
Using sprockets (2.1.3)
Using actionpack (3.2.6)
Using mime-types (1.18)
Using polyglot (0.3.3)
Using treetop (1.4.10)
Using mail (2.4.4)
Using actionmailer (3.2.6)
Using arel (3.0.2)
Using tzinfo (0.3.33)
Using activerecord (3.2.6)
Using activeresource (3.2.6)
Using bundler (1.0.21)
Using coffee-script-source (1.3.3)
Using execjs (1.4.0)
Using coffee-script (2.2.0)
Using rack-ssl (1.3.2)
Using json (1.7.3)
Using rdoc (3.12)
Using thor (0.15.2)
Using railties (3.2.6)
Using coffee-rails (3.2.2)
Using diff-lcs (1.1.3)
Using jquery-rails (2.0.2)
Using mysql2 (0.3.11)
Using rails (3.2.6)
Using rspec-core (2.10.1)
Using rspec-expectations (2.10.0)
Using rspec-mocks (2.10.1)
Using rspec (2.10.0)
Using rspec-rails (2.10.1)
Using sass (3.1.19)
Using sass-rails (3.2.5)
Installing spork (0.9.2)
Using uglifier (1.2.4)
Your bundle is complete! It was installed into ./vendor/bundle
次に、Spork の初期設定を行います。
$ bundle exec spork --bootstrap
spec/spec_helper.rb に Spork 実行用のコードが追記されますので、RSpec のテストが Spork によって実行されるよう修正します。
下記のように、もともとあった RSpec の設定を Spork.prefork ブロックの中に移動します。
$ vim spec/spec_helper.rb
require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
#require 'spork/ext/ruby-debug'
Spork.prefork do
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
end
end
Spork.each_run do
# This code will be run each time you run your specs.
end
Spork が実行できるよう、.rspec に --drb オプションを追記します。
$ vim .rspec
--colour
--drb
Spork を使ってテストを実行
Spork を別コンソールで起動しておきます。
$ rvm 1.9.2-p290@rails32
$ cd ~/projects/blog
$ bundle exec spork
Using RSpec
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!
RSpec のテストを実行します。
$ bundle exec rspec spec
..............................
Finished in 0.88647 seconds
30 examples, 0 failures
Spork にてあらかじめ Rails のコードがロードされるため、テスト実行までの時間が短くなります。
Fixture を使う
テストデータを使ったテストを書いてみます。
まず、テストデータを用意する前にテストコードを実装します。
$ vim spec/models/article_spec.rb
# coding: utf-8
require 'spec_helper'
describe Article do
# 追記ここから
fixtures(:all)
describe '.all' do
subject { Article.all }
it { should have(1).items }
end
# 追記ここまで
describe '.new' do
context 'given valid attributes' do
subject { Article.new(:title => 'a', :content => 'a') }
it { should be_valid }
end
context 'given null title' do
subject { Article.new(:content => 'a') }
it { should have(1).errors_on(:title) }
end
end
end
テストを実行し、「失敗」することを確認します。
$ bundle exec rspec spec
................F..............
Failures:
1) Article.all
Failure/Error: it { should have(1).items }
expected 1 items, got 0
# ./spec/models/article_spec.rb:10:in `block (3 levels) in <top (required)>'
Finished in 1.3 seconds
31 examples, 1 failure
Failed examples:
rspec ./spec/models/article_spec.rb:10 # Article.all
テストデータを用意します。
$ mkdir spec/fixtures
$ vim spec/fixtures/articles.yml
article_001:
title: 'MyString'
content: 'MyText'
テストを実行します。
$ bundle exec rspec spec
...............................
Finished in 0.82728 seconds
31 examples, 0 failures
テスト実行時に Fixture がテスト DB に読み込まれるようになりましたので、結果は「成功」となります。
Fixture の代わりに FactoryGirl を使う
Fixture Replacement の代表格、FactoryGirl を使ってみます。
テストデータが DB に残ってしまい、意図しないテスト結果となることを防ぐため、DatabaseCleaner も併せてインストールします。
$ vim Gemfile
group :development, :test do
gem 'rspec-rails', '2.10.1'
gem 'spork', '0.9.2'
gem 'factory_girl_rails', '3.4.0'
gem 'database_cleaner', '0.8.0'
end
$ bundle
Fetching source index for https://rubygems.org/
Using rake (0.9.2.2)
Using i18n (0.6.0)
Using multi_json (1.3.6)
Using activesupport (3.2.6)
Using builder (3.0.0)
Using activemodel (3.2.6)
Using erubis (2.7.0)
Using journey (1.0.4)
Using rack (1.4.1)
Using rack-cache (1.2)
Using rack-test (0.6.1)
Using hike (1.2.1)
Using tilt (1.3.3)
Using sprockets (2.1.3)
Using actionpack (3.2.6)
Using mime-types (1.18)
Using polyglot (0.3.3)
Using treetop (1.4.10)
Using mail (2.4.4)
Using actionmailer (3.2.6)
Using arel (3.0.2)
Using tzinfo (0.3.33)
Using activerecord (3.2.6)
Using activeresource (3.2.6)
Using bundler (1.0.21)
Using coffee-script-source (1.3.3)
Using execjs (1.4.0)
Using coffee-script (2.2.0)
Using rack-ssl (1.3.2)
Using json (1.7.3)
Using rdoc (3.12)
Using thor (0.15.2)
Using railties (3.2.6)
Using coffee-rails (3.2.2)
Installing database_cleaner (0.8.0)
Using diff-lcs (1.1.3)
Installing factory_girl (3.4.0)
Installing factory_girl_rails (3.4.0)
Using jquery-rails (2.0.2)
Using mysql2 (0.3.11)
Using rails (3.2.6)
Using rspec-core (2.10.1)
Using rspec-expectations (2.10.0)
Using rspec-mocks (2.10.1)
Using rspec (2.10.0)
Using rspec-rails (2.10.1)
Using sass (3.1.19)
Using sass-rails (3.2.5)
Using spork (0.9.2)
Using uglifier (1.2.4)
Your bundle is complete! It was installed into ./vendor/bundle
FactoryGirl および DatabaseCleaner を設定
一旦 Spork を止め、DatabaseCleaner によりテスト実行毎にテスト DB のデータが削除されるようにします。
また、Factory が追加された際に Spork を再起動しなくても読み込めるよう設定します。
$ vim spec/spec_helper.rb
RSpec.configure do |config|
# ## Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# 追記ここから
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
# 追記ここまで
end
Spork.each_run do
# This code will be run each time you run your specs.
FactoryGirl.reload # 追記
end
再び Spork を起動します。
$ bundle exec spork
Factory を使用してテストを実行
テストコードでは Fixture を読み込まないようにし、FactoryGirl を使うようにします。
$ vim spec/models/article_spec.rb
# coding: utf-8
require 'spec_helper'
describe Article do
# fixtures(:all) を削除
describe '.all' do
# 追記ここから
before do
FactoryGirl.create(:article)
end
# 追記ここまで
subject { Article.all }
it { should have(1).items }
end
describe '.new' do
context 'given valid attributes' do
subject { Article.new(:title => 'a', :content => 'a') }
it { should be_valid }
end
context 'given null title' do
subject { Article.new(:content => 'a') }
it { should have(1).errors_on(:title) }
end
end
end
テストを実行し、「失敗」することを確認します。
$ bundle exec rspec spec
................F..............
Failures:
1) Article.all
Failure/Error: FactoryGirl.create(:article)
ArgumentError:
Factory not registered: article
# ./spec/models/article_spec.rb:8:in `block (3 levels) in <top (required)>'
Finished in 1.14 seconds
31 examples, 1 failure
Failed examples:
rspec ./spec/models/article_spec.rb:12 # Article.all
Factory を自動生成します。
$ bundle exec rails g factory_girl:model article title:string content:text
create spec/factories/articles.rb
spec/factories 以下に Factory が生成されます。
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :article do
title "MyString"
content "MyText"
end
end
再度、テストを実行します。
$ bundle exec rspec spec
...............................
Finished in 1.23 seconds
31 examples, 0 failures
Fixture の代わりに FactoryGirl を使ってテストを実行することができました。
RSpec 実行時のオプションを指定(おまけ)
RSpec では、テスト実行時の見た目を変更することができます。
$ vim .rspec
--colour
--drb
--format d
テストを実行すると、下記のように表示されます。
$ bundle exec rspec spec
ArticlesController
GET index
assigns all articles as @articles
GET show
assigns the requested article as @article
GET new
assigns a new article as @article
GET edit
assigns the requested article as @article
POST create
with valid params
creates a new Article
assigns a newly created article as @article
redirects to the created article
with invalid params
assigns a newly created but unsaved article as @article
re-renders the 'new' template
PUT update
with valid params
updates the requested article
assigns the requested article as @article
redirects to the article
with invalid params
assigns the article as @article
re-renders the 'edit' template
DELETE destroy
destroys the requested article
redirects to the articles list
Article
.all
should have 1 items
.new
given valid attributes
should be valid
given null title
should have 1 errors on :title
Articles
GET /articles
works! (now write some real specs)
ArticlesController
routing
routes to #index
routes to #new
routes to #show
routes to #edit
routes to #create
routes to #update
routes to #destroy
articles/edit
renders the edit article form
articles/index
renders a list of articles
articles/new
renders new article form
articles/show
renders attributes in <p>
Finished in 1.28 seconds
31 examples, 0 failures
以上でチュートリアルは終わりです。
本格的にアプリケーションを開発するには足りないところが多すぎますが、まずはテストを書き始めるきっかけになればと思います。