最近のテスト事情
むかしに比べると、かなりテストが書けるようになってきたし、TDDもだんだん慣れてきた。最近テスト書いてて便利だと思ったことについてメモっておく。
スタブを使ってbefore_filterをスキップする
describe 'GET index' do context 'ログインしている場合' do before(:each) do controller.any_instances.stub(:authenticate_user).and_return(true) end it 'hogehogeなfugafugaを取得する' do get index, params assigns[:fugafuga].should be_hogehoge end end end
ログイン判定のような、リクエストをはじく処理をbefore_filter
で実装することがよくあるけど、そういうコントローラーをテストする場合、スタブが便利だということにようやく気づいた。スタブによって、メソッドの中身をごまかして好きな値を返すようにできる。だから、before_filter
をスキップしたい場合は、とにかくスタブしてtrue
を返すようにしとけばいい。skip_before_filter
でもスキップすることはできるけど、僕はスタブを使う方が好み。
FactoryGirlを使いこなす
FactoryGirl.define do factory :user do # 連番を使えばuniquenessのバリデーションにかからなくなる sequence(:name) {|n| "user #{n}" } sequence(:email) {|n| "user#{n}@example.com" } age { rand(18..30) } after(:build) do |user| # 余計なデータを作るコールバックがあればスキップできる User.skip_callback(:after, :create, :create_data) end # ネストしたfactoryで上書きできる factory :naoty do name 'naoty' email 'naoty@example.com' age 18 end # traitで属性のグループに名前をつけられる trait :resigned do resigned_at { Time.now } end end end user = FactoryGirl.create(:user) p user.name #=> "user 1" naoty = FactoryGirl.create(:naoty) p naoty.name #=> "naoty" resigned_user = FactoryGirl.create(:user, :resigned) p resigned_user.name #=> "user 2" p resigned_user.resigned_at #=> "2013-01-19 00:43:59 +0900"
factory_girl
はテスト用のデータを簡単につくるためのgem。似たようなgemは他にもあるけど、こういうgemを使うと、テストデータを作るロジックとテストコードを分離できる。なので、いろんなテストで使われるテストデータを重複なく簡単に作ることができる。
FactoryGirlでテストデータを作成するときに、よくひっかかるのがバリデーションやafter_save
などのコールバック内の余計な処理だと思う。こういう鬱陶しい処理は、FactoryGirlのコールバックを使ってスキップしてる。
trait
は特殊なデータを作る場合にすごく役に立つ。上記の例のような「退会ユーザー」をテストに使いたいときなど、特殊なデータの属性をひとまとめにしてFactoryGirl.create(:user, :resigned)
のように簡単に作成できる。
changeマッチャが便利
describe '#resign' do let(:user) { FactoryGirl.create(:user) } it 'resigned_atを更新する' do lambda { user.resign }.should change(user, :resigned?).from(false).to(true) end end
モデルの更新系のメソッドをテストするとき、change
マッチャが非常に便利。上の例で言うと、user.resigned?
の結果がlambda内の処理を実行した前後でfalse
からtrue
に変わることをテストしている。