これからテストを書き始めたい人のための Rails+RSpec+Spork+FactoryGirl チュートリアル（その１）


「今まで 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
```

```ruby: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/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
```

```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
```

```ruby: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
```

```ruby: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
```

```ruby: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
```

```ruby: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
```

```ruby: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
```

```ruby: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
```

バリデーションを実装したため、結果は「成功」となります。

その２へ続きます。