この RSpec には 
夢があるッ!! 
inspire the るびま&ジョジョ 
今さらながら 
RSpecに入門してみた
さくらば@ZARU 
株式会社ベーシック&株式会社フルセイル 
TwitterとQiita、やってます。 
楽しいことは誰にも譲らないで、自分で楽しむタイプです。 
! カレーと寿司とラーメンが好き 
! プログラミングが好き 
! どんな言語も好きだけど、JavaScriptが大好き 
! 娘、産まれて育ってます
今日は 
Railsアプリで、RSpec使った 
テストに入門した話をします。
その前に…
宣伝!
今月もQiita投稿大会やってます! 
ルール 
! 月内に獲得したストック数が一番多いものが優勝 
! 月内に投稿された記事で咲いたストック数は優秀賞 
! 9月は優勝/優秀賞には天ぷら 
! さくらばが優勝したら、割り勘ね…
宣伝ここまで
はじめます
? 
そもそもテストって何?
…っていう話は今回はしません。
まず、テストコードを 
書けるようになってから考えよう。 
道具について語るには、まず道具を知ることから。
TDD is dead. Long live testing. 
Rails作者:David Heinemeier Hansson
偏った原理主義に走るなよ、環境は変わってきているんだし 
テストのあり方も変えていこうぜ。っていう話…と思ってます。
「磯野ー!テストしようぜ!」
テストの原則 
! テストは信頼できるものであること 
! テストは簡単に書けること 
! テストは簡単に理解できること 
RSpecによるRailsテスト入門より引用
前置きは終了。
この本を 
ベースに 
話します。 
分かりやすく 
とてもいい本です。 
RSpec3用にリニューアルも 
予定しているっぽいです。 
買いましょう。
RSpecに 
入門してみる
今日、話すこと 
! Gemfile + RSpec設定 
! モデルスペック 
! ファクトリ 
! コントローラスペック
Gemfile
group :development, :test do 
gem "rspec-rails", "~> 2.14.0" 
gem "factory_girl_rails", "~> 4.2.1" 
end 
group :test do 
gem "faker", "~> 1.1.2" 
gem "capybara", "~> 2.1.0" 
gem "database_cleaner", "~> 1.0.1" 
gem "launchy", "~> 2.3.0" 
gem "selenium-webdriver", "~> 2.39.0" 
end 
RSpec3が2014年6月くらいにリリースされているけど、今回は2系を使用。 
3については下記ページ参照。 
http://qiita.com/yujinakayama/items/a1d31b2caa35642e8e69 
http://nilp.hatenablog.com/entry/2014/05/28/003335
紹介
rspec-rails 
RSpec本体 + RailsでRSpec使うためのもろもろ。 
factory_girl_rails 
標準フィクスチャをもっと便利なファクトリとして提供してくれる。 
テストデータをさくっと作るのにイケてるガール。 
faker 
名前・メールアドレスなどダミーデータを作ってくれる。 
日本名に対応したFaker::Japaneseというのもある。 
capybara 
Webアプリテストをコードレベルで行うことができる。 
色々連携する。
database_cleaner 
まっさらな環境で、テストが実行できるように 
テストデータを消してくれる。 
launchy 
好きなタイミングでブラウザを開くことができる。 
selenium-webdriver 
Seleniumでブラウザ上テストを実行できる。 
他にもGuard・RuboCopとかテスト周りで便利なGemある
RSpecの設定
$ bundle exec rails g rspec:install 
create .rspec 
create spec 
create spec/spec_helper.rb 
.rspec 
--color 
--format documentation 
RSpecの実行結果を、見やすく楽しくしてくれる設定
こんな感じ
/config/application.rb 
config.generators do |g| 
g.test_framework :rspec, 
fixtures: true, 
view_specs: false, 
helper_specs: false, 
routing_specs: false, 
controller_specs: true, 
request_specs: false 
g.fixture_replacement :factory_girl, dir: "spec/factories” 
end 
ジェネレータで作成されるスペックファイルの設定。 
ビューはRSpecではテストしないので作らない。 
フィクスチャの代わりにファクトリを使う。
モデル 
スペック
名前とメールアドレスを持つContactという 
モデルがあったとして、Contactモデルの 
スペックを書いてみる。 
Contact 
name 
email 
さくっと、scaffoldしておく。 
$ bundle exec rails g  
scaffold contact  
name:string  
email:string:unique 
$ bundle exec rake db:migrate 
Rails4.1からdb:test:cloneなどは、自動で行われるので必要なくなった
テストって 
どう書くの?
モデルスペックを書いてみる
$vi /spec/models/contact_spec.rb 
require 'spec_helper' 
describe Contact do 
it "全てのデータが有効な状態であること" 
it "名前がなければ無効な状態であること" 
it "メールがなければ無効な状態であること" 
it "重複したメールアドレスなら無効な状態であること" 
end 
まず、期待する動作をアウトライン的に 
書き出してみる。
おもむろにテストを実行してみる。 
$ bundle exec rspec
scaffoldで勝手に作られた 
コントローラースペックが動いてます。 
+今作ったContactモデルのも。 
当然、テストの中身を書いてないので、 
pendingという状態になってます。
テストコードを書いてみる
require 'spec_helper' 
describe Contact do 
it "全てのデータが有効な状態であること" do 
contact = Contact.new( 
name: 'zaru', 
email: 'zaru@example.com' 
) 
expect(contact).to be_valid 
end 
end 
Contactモデルを作成する(DBには保存しない) 
expect(オブジェクト).to 期待するもの、でテストできる 
RSpec2.11から、オブジェクト.should が expect() に変更になりました
おもむろにテストを実行してみる。 
$ bundle exec rspec
テスト通ってる!
でも、実はこれ…
中身ないContactオブジェクト 
でもテスト通る!
require 'spec_helper' 
describe Contact do 
it "全てのデータが有効な状態であること" do 
contact = Contact.new() 
expect(contact).to be_valid 
end 
end 
通る!
なので、バリデーション設定しましょう。
$vi /app/models/contact.rb 
class Contact < ActiveRecord::Base 
validates :name, presence: true 
validates :email, presence: true, uniqueness: true 
end 
nameもemailも必須で、emailはユニーク扱い。
中身のないContactオブジェクトのまま… 
再度、実行!
失敗してる!
1個ずつバリデーションのテストしましょう
require 'spec_helper' 
describe Contact do 
it "名前がなければ無効な状態であること" do 
contact = Contact.new( 
name: nil 
) 
expect(contact).to have(1).errors_on(:name) 
end 
it "メールがなければ無効な状態であること" do 
contact = Contact.new( 
email: nil 
) 
expect(contact).to have(1).errors_on(:email) 
end 
end 
nameのデータを空にして 
エラーがでるかテスト
重複のテストもしましょう
require 'spec_helper' 
describe Contact do 
it "重複したメールアドレスなら無効な状態であること" do 
Contact.create( 
name: 'zaru', 
email: 'zaru@example.com' 
) 
contact = Contact.new( 
name: 'tofu', 
email: 'zaru@example.com' 
) 
expect(contact).to have(1).errors_on(:email) 
end 
end 
createは、テストデータを 
DBに登録をしてくれる。
CPC設定完了 
ニューラルリンケージ 
イオン濃度正常 
メタ運動野パラメータ更新 
原子炉臨界 
パワーフロー正常 
全システムオールグリーン 
ストライクフリーダム、システム起動
テストコードの書き方ポイント
! スペックはアウトラインと考える 
! describeでまとめて、itで期待する結果を書く 
contextは、状態の変化で使い分ける 
! itには1個につき、結果を1つだけ期待する 
! itは、◯◯である/◯◯すること、などの動詞にする 
! 起きてほしいことと、起きてほしくないことを書く 
! 境界値テストをすること 
◯文字以上~◯文字以内の場合、その前後の値で
ついでに、 
インスタンスメソッドをテストしてみる
名前付きメールアドレスを返すメソッド 
class Contact < ActiveRecord::Base 
def nameAddr 
"%s<%s>" % [name, email] 
end 
end
require 'spec_helper' 
describe Contact do 
it "名前付きメールアドレスを返すこと" do 
contact = Contact.new( 
name: 'zaru', 
email: 'zaru@example.com' 
) 
expect(contact.nameAddr).to 
eq 'zaru<zaru@example.com>' 
end 
end 
nameAddrを実行して、かえってくる文字列を比較する
テストコードをDRYに
beforeフックで、まとめる 
require 'spec_helper' 
describe Contact do 
before :each do 
@contact = Contact.new( 
name: 'zaru', 
email: 'zaru@example.com' 
) 
end 
end 
Contactオブジェクトをインスタンス変数に格納 
beforeは、describe中の各テストの前に実行される
ファクトリ
Factory 
Girl 
ひな形から 
テストデータを 
量産できる可愛い子
Contactファクトリ作る
$vi /spec/factories/contacts.rb 
FactoryGirl.define do 
factory :contact do 
name "zaru" 
sequence(:email) { |n| "zaru#{n}@example.com" } 
end 
end 
内部でインクリメンタルして、 
違うメールアドレスを作ってくれる
テストコードを書きなおしてみる
it "全てのデータが有効な状態であること" do 
contact = Contact.new( 
name: 'zaru', 
email: 'zaru@example.com' 
) 
expect(contact).to be_valid 
end 
it "全てのデータが有効な状態であること" do 
expect(FactoryGirl.build(:contact)).to be_valid 
end 
こうなる 
FactoryGirl.buildで、Contact.newと同様のことができる。 
FactoryGirl.createで、Contact.createと同じ。
FactoryGirlって 
打つの面倒
省略できます
$vi /spec/spec_helper.rb 
RSpec.configure do |config| 
config.include FactoryGirl::Syntax::Methods 
end 
it "全てのデータが有効な状態であること" do 
expect(build(:contact)).to be_valid 
end 
FactoryGirl省略できる
はい…
今日はここまで!
サンプルコードはGitHubにあります。 
https://github.com/zaru/rials_rspec_sample
次回
! コントローラースペック 
! Capybaraさん 
! REST WebAPIテスト
予定は 
未定…
$ shutdown -h now

今さらながらRSpecに入門してみた