7. FactoryGirl とモデルのテスト

FactoryGirl の記述

モデルを作成した時点で FactoryGirl の雛形は自動に作成してくれる。今回は、テストのために属性値だけ修正しておく。

FactoryGirl.define do
  factory :year do
    year 2014
    default_year true
  end
end

昨日、spec/factories を監視するように設定したため、保存すると自動的にテストが作動する。

17:23:33 - INFO - Running: spec/models/year_spec.rb
No examples found.

Finished in 0.00028 seconds (files took 1.85 seconds to load)
0 examples, 0 failures

まだテストは書いていないので、何も起こらない。

モデルのテスト記述

RSpec では let という遅延評価と保持の仕組みがある。以下のように定義する。

RSpec.describe Year, :type => :model do
  let(:year) { FactoryGirl.build(:year) }
end

このようにすると year と記述すると FactoryGirl.build(:year) が実行される。同じテスト内では、その値が保持されるので FactoryGirl.build は発火しない。Ruby で真面目に書くとこんな感じになる。

def year; @year ||= FactoryGirl.build(:year); end

余計なインスタンス変数ができない分、let の方がわかりやすい。

RSpec のブロック内に一つテストを追加してみる。FactoryGirl で year が生成できるかどうかをテストするものである。

  it { expect(year).to be_valid }

保存すると、Guard が作動する。これは問題ないためテストは通過する。

Year
  should be valid

Finished in 0.01289 seconds (files took 1.69 seconds to load)
1 example, 0 failures

it の第一引数を省略すると、テスト内容から自動的に仕様文書を作成してくれる。なるべく楽をするように、これからは it の引数を省略できるようなテストを記述したい。

テストを追加

year と default_year 属性は nil 禁止なので、属性が nil の時に有効でないことを確認する。

  [ :year, :default_year ].each do |key|
    context "when #{key} is nil" do
      it { expect(FactoryGirl.build(:year, key => nil)).to_not be_valid }
    end
  end

結果は以下のようになる。

18:21:16 - INFO - Running: spec/models/year_spec.rb

Year
  should be valid
  when year is nil
    should not be valid (FAILED - 1)
  when default_year is nil
    should not be valid (FAILED - 2)

Failures:

  1) Year when year is nil should not be valid
     Failure/Error: it { expect(FactoryGirl.build(:year, key => nil)).to_not be_valid }
       expected #<Year id: nil, year: nil, default_year: true, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/year_spec.rb:9:in `block (4 levels) in <top (required)>'

  2) Year when default_year is nil should not be valid
     Failure/Error: it { expect(FactoryGirl.build(:year, key => nil)).to_not be_valid }
       expected #<Year id: nil, year: 2014, default_year: nil, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/year_spec.rb:9:in `block (4 levels) in <top (required)>'

Finished in 0.01561 seconds (files took 1.82 seconds to load)
3 examples, 2 failures

Failed examples:

rspec ./spec/models/year_spec.rb:9 # Year when year is nil should not be valid
rspec ./spec/models/year_spec.rb:9 # Year when default_year is nil should not be valid

実装を記述していないので、当然エラーとなる。

app/models/year.rb に validation を追加する(default_year は boolean なので presence ではテストできないのを忘れていたので後日修正)。

class Year < ActiveRecord::Base
  validates :year, presence:true
  validates :default_year, inclusion: { in: [true, false] }
end

この結果、テストが無事に通過する。

18:57:27 - INFO - Running: spec/models/year_spec.rb

Year
  should be valid
  when year is nil
    should not be valid
  when default_year is nil
    should not be valid

Finished in 0.30622 seconds (files took 1.86 seconds to load)
3 examples, 0 failures

同様に unique 性のテストをする。

  context "when duplicate year" do
    it { year.save; expect(FactoryGirl.build(:year)).to_not be_valid }
  end

この結果エラーとなることが確認できる。

19:12:20 - INFO - Running: spec/models/year_spec.rb

Year
  should be valid
  when year is nil
    should not be valid
  when default_year is nil
    should not be valid
  when duplicate year
    should not be valid (FAILED - 1)

Failures:

  1) Year when duplicate year should not be valid
     Failure/Error: it { year.save; expect(FactoryGirl.build(:year)).to_not be_valid }
       expected #<Year id: nil, year: 2014, default_year: true, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/year_spec.rb:15:in `block (3 levels) in <top (required)>'

Finished in 0.30945 seconds (files took 1.65 seconds to load)
4 examples, 1 failure

Failed examples:

rspec ./spec/models/year_spec.rb:15 # Year when duplicate year should not be valid

このテストを通過させるために、uniqueness の validation を追加する。

  validates :year, presence:true, uniqueness:true

この結果、テストが無事通過する。

19:12:51 - INFO - Running: spec/models/year_spec.rb

Year
  should be valid
  when year is nil
    should not be valid
  when default_year is nil
    should not be valid
  when duplicate year
    should not be valid

Finished in 0.29406 seconds (files took 1.62 seconds to load)
4 examples, 0 failures

今日はここまで。
written by iHatenaSync