「今まで Rails でテストを疎かにしてきたけど、これからはちゃんとテストを書きたいな」と思っている人のためのチュートリアルです。
(自分用のメモでもあります。。)
環境
- Mac OS X 10.6.8
- Ruby 1.9.2
- Rails 3.2.6
- RSpec 2.10.0
- Spork 0.9.2
- FactoryGirl 3.4.0
- DatabaseCleaner 0.8.0
- MySQL 5.5.15
プロジェクトを生成
チュートリアル用の Rails 3.2 アプリケーションを生成します。
Rails 標準の Test::Unit ではなく、RSpec を使います。
$ rvm 1.9.2-p290@rails32
$ mkdir ~/projects && cd ~/projects
$ rails new blog -T -d mysql --skip-bundle && cd blog
create
create README.rdoc
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/assets/images/rails.png
create app/assets/javascripts/application.js
create app/assets/stylesheets/application.css
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create app/mailers
create app/models
create app/views/layouts/application.html.erb
create app/mailers/.gitkeep
create app/models/.gitkeep
create config
create config/routes.rb
create config/application.rb
create config/environment.rb
create config/environments
create config/environments/development.rb
create config/environments/production.rb
create config/environments/test.rb
create config/initializers
create config/initializers/backtrace_silencers.rb
create config/initializers/inflections.rb
create config/initializers/mime_types.rb
create config/initializers/secret_token.rb
create config/initializers/session_store.rb
create config/initializers/wrap_parameters.rb
create config/locales
create config/locales/en.yml
create config/boot.rb
create config/database.yml
create db
create db/seeds.rb
create doc
create doc/README_FOR_APP
create lib
create lib/tasks
create lib/tasks/.gitkeep
create lib/assets
create lib/assets/.gitkeep
create log
create log/.gitkeep
create public
create public/404.html
create public/422.html
create public/500.html
create public/favicon.ico
create public/index.html
create public/robots.txt
create script
create script/rails
create tmp/cache
create tmp/cache/assets
create vendor/assets/javascripts
create vendor/assets/javascripts/.gitkeep
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.gitkeep
create vendor/plugins
create vendor/plugins/.gitkeep
RSpecを使う
RSpec を使うため、Gemfile に追記します。
$ vim Gemfile
group :development, :test do
gem 'rspec-rails', '2.10.1'
end
gem をインストールします。
$ bundle --path vendor/bundle
Fetching source index for https://rubygems.org/
Installing rake (0.9.2.2)
Installing i18n (0.6.0)
Installing multi_json (1.3.6)
Installing activesupport (3.2.6)
Installing builder (3.0.0)
Installing activemodel (3.2.6)
Installing erubis (2.7.0)
Installing journey (1.0.4)
Installing rack (1.4.1)
Installing rack-cache (1.2)
Installing rack-test (0.6.1)
Installing hike (1.2.1)
Installing tilt (1.3.3)
Installing sprockets (2.1.3)
Installing actionpack (3.2.6)
Installing mime-types (1.18)
Installing polyglot (0.3.3)
Installing treetop (1.4.10)
Installing mail (2.4.4)
Installing actionmailer (3.2.6)
Installing arel (3.0.2)
Installing tzinfo (0.3.33)
Installing activerecord (3.2.6)
Installing activeresource (3.2.6)
Using bundler (1.0.21)
Installing coffee-script-source (1.3.3)
Installing execjs (1.4.0)
Installing coffee-script (2.2.0)
Installing rack-ssl (1.3.2)
Installing json (1.7.3) with native extensions
Installing rdoc (3.12)
Installing thor (0.15.2)
Installing railties (3.2.6)
Installing coffee-rails (3.2.2)
Installing diff-lcs (1.1.3)
Installing jquery-rails (2.0.2)
Installing mysql2 (0.3.11) with native extensions
Installing rails (3.2.6)
Installing rspec-core (2.10.1)
Installing rspec-expectations (2.10.0)
Installing rspec-mocks (2.10.1)
Installing rspec (2.10.0)
Installing rspec-rails (2.10.1)
Installing sass (3.1.19)
Installing sass-rails (3.2.5)
Installing uglifier (1.2.4)
Your bundle is complete! It was installed into ./vendor/bundle
bundle install
は bundle
と省略できます。
また、--path
オプションにより gem のインストール先を指定すると、.bundle/config ファイルが生成されます。
---
BUNDLE_PATH: vendor/bundle
BUNDLE_DISABLE_SHARED_GEMS: '1'
BUNDLE_PATH
が設定されるため、次回以降 bundle install
する際に --path
は必要ありません。
次に、RSpec の初期設定を行います。
$ bundle exec rails g rspec:install
create .rspec
create spec
create spec/spec_helper.rb
これで、Rails で RSpec が使えるようになりました。
アプリケーションの雛形を生成
例として、Article モデルを扱うコードを Scaffold で生成します。
$ bundle exec rails g scaffold article title:string content:text
invoke active_record
create db/migrate/20120616114653_create_articles.rb
create app/models/article.rb
invoke rspec
create spec/models/article_spec.rb
invoke resource_route
route resources :articles
invoke scaffold_controller
create app/controllers/articles_controller.rb
invoke erb
create app/views/articles
create app/views/articles/index.html.erb
create app/views/articles/edit.html.erb
create app/views/articles/show.html.erb
create app/views/articles/new.html.erb
create app/views/articles/_form.html.erb
invoke rspec
create spec/controllers/articles_controller_spec.rb
create spec/views/articles/edit.html.erb_spec.rb
create spec/views/articles/index.html.erb_spec.rb
create spec/views/articles/new.html.erb_spec.rb
create spec/views/articles/show.html.erb_spec.rb
invoke helper
create spec/helpers/articles_helper_spec.rb
create spec/routing/articles_routing_spec.rb
invoke rspec
create spec/requests/articles_spec.rb
invoke helper
create app/helpers/articles_helper.rb
invoke rspec
invoke assets
invoke coffee
create app/assets/javascripts/articles.js.coffee
invoke scss
create app/assets/stylesheets/articles.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
DB の設定を行います。
$ vim config/database.yml
common: &common
adapter: mysql2
encoding: utf8
reconnect: false
pool: 5
username: root
password: yourpassword
host: localhost
development:
<<: *common
database: blog_dev
test:
<<: *common
database: blog_test
production:
<<: *common
database: blog
DB およびテーブルを作成します。
$ bundle exec rake db:create
$ bundle exec rake db:migrate
== CreateArticles: migrating =================================================
-- create_table(:articles)
-> 0.0896s
== CreateArticles: migrated (0.0897s) ========================================
これで、アプリケーションが動作するようになりました。
テストを実行
ひとまず、テストを実行してみます。
$ bundle exec rake
下記のコマンドでも実行できます。
$ bundle exec rspec spec
2つのテストが Pending となっていることがわかります。
- spec/helpers/articles_helper_spec.rb
- spec/models/article_spec.rb
Pending のテストコードを削除します。
$ vim spec/helpers/articles_helper_spec.rb
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the ArticlesHelper. For example:
#
# describe ArticlesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# helper.concat_strings("this","that").should == "this that"
# end
# end
# end
describe ArticlesHelper do
# pending "add some examples to (or delete) #{__FILE__}" を削除
end
$ vim spec/models/article_spec.rb
require 'spec_helper'
describe Article do
# pending "add some examples to (or delete) #{__FILE__}" を削除
end
テストを実行し、すべてのテストが「成功」となっていることを確認します。
$ bundle exec rspec spec
............................
Finished in 0.80978 seconds
28 examples, 0 failures
テストコードを実装
正常にモデルが new されることを検証するテストを書いてみます。
$ vim spec/models/article_spec.rb
# coding: utf-8
require 'spec_helper'
describe Article do
describe '.new' do
context 'given valid attributes' do
subject { Article.new(:title => 'a', :content => 'a') }
it { should be_valid }
end
end
end
テストを実行して「成功」となっていることを確認します。
$ bundle exec rspec spec
.............................
Finished in 0.69964 seconds
29 examples, 0 failures
ひとつのファイルのみ実行したい場合は、下記のように実行します。
$ bundle exec rspec spec/models/article_spec.rb
.
Finished in 0.09452 seconds
1 example, 0 failures
続いて、Article モデルの title は必須となることを想定してテストを書きます。
$ vim spec/models/article_spec.rb
# coding: utf-8
require 'spec_helper'
describe Article do
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
$ vim spec/controllers/articles_controller_spec.rb
def valid_attributes
{:title => 'a', :content => 'a'}
end
プロダクトコードを実装していませんが、試しにテストを実行してみます。
$ bundle exec rspec spec
.................F............
Failures:
1) Article.new given null title
Failure/Error: it { should have(1).errors_on(:title) }
expected 1 errors on :title, got 0
# ./spec/models/article_spec.rb:14:in `block (4 levels) in <top (required)>'
Finished in 0.74384 seconds
30 examples, 1 failure
Failed examples:
rspec ./spec/models/article_spec.rb:14 # Article.new given null title
結果は当然「失敗」となります。
それでは、Article モデルにバリデーションを実装します。
$ vim app/models/article.rb
# coding: utf-8
class Article < ActiveRecord::Base
attr_accessible :content, :title
validates(:title, :presence => true)
end
テストを実行します。
$ bundle exec rspec spec
..............................
Finished in 0.87777 seconds
30 examples, 0 failures
バリデーションを実装したため、結果は「成功」となります。
その2へ続きます。