工場の工員が製品を製造するというFactory Methodパターンのテストを考えてみる。
工員と製品は工場に所属していて、工員が製品を製造すると、工場の在庫(製品)が増える。
まずは必要なモデルを生成する。
rails g model koujou name:string rails g model kouin koujou:references name:string rails g model seihin koujou:references seihin_num:integer rake db:migrate工場モデルに工員、製品間の1対多の関係を設定しておく。
class Koujou < ActiveRecord::Base has_many :kouins has_many :seihins end次に、工員モデルに製品の製造メソッドを追加する。
class Kouin < ActiveRecord::Base
belongs_to :koujou
def seizou
seihin = Seihin.new
seihin.koujou_id = koujou_id
max_seihin_num = Seihin.maximum(:seihin_num)
if max_seihin_num.blank?
seihin.seihin_num = 1
else
seihin.seihin_num = max_seihin_num + 1
end
return seihin
end
end
下記のような感じでRspecのテストコードを記述してみる。テストコード内でsaveしてもDBにはcommitされないので、製造後の製品の配列のサイズの確認は常に1で問題ない。
require 'spec_helper'
describe Koujou do
it '製品製造後の在庫(製品)増加の確認.' do
koujou = Koujou.new
koujou.name = 'テスト'
koujou.save
p koujou.seihins
kouin = Kouin.new
kouin.koujou_id = koujou.id
kouin.name = '浜松浜子'
kouin.save
seihin = kouin.seizou
seihin.save
p koujou.seihins
expect(koujou).to have(1).seihins
end
end
ところが、テストが通らない。製造後の製品の配列のサイズは0のままである。
bundle exec rspec spec/models/koujou_spec.rb
Rack::File headers parameter replaces cache_control after Rack 1.5.
[]
[]
[31mF [0m
Failures:
1) Koujou 製品製造後の在庫(製品)増加の確認.
[31mFailure/Error: [0m [31mexpect(koujou).to have(1).seihins [0m
[31mexpected 1 seihins, got 0 [0m
[36m # ./spec/models/koujou_spec.rb:23:in `block (2 levels) in <top (requir
ed)>' [0m
Finished in 0.53903 seconds
[31m1 example, 1 failure [0m
Failed examples:
[31mrspec ./spec/models/koujou_spec.rb:6 [0m [36m# Koujou 製品製造後の在庫(製
品)増加の確認. [0m
Randomized with seed 17099
よく考えてみると前回の記事と関連した問題である。子モデルの値を参照する毎にDBから最新の値を取得しているのではなく、インスタンスに変更が加わらない限りは一度DBから参照した値をそのまま保持しているので、最初に参照した時点で配列が空になっている状態をその後も返しているわけである。
下記のように、途中で子モデルの値を確認する余計なコードを外して再度実行すると、チェックの時点で初めてDBから値を取得するのでテストが通るようになる。
require 'spec_helper'
describe Koujou do
it '製品製造後の在庫(製品)増加の確認.' do
koujou = Koujou.new
koujou.name = 'テスト'
koujou.save
kouin = Kouin.new
kouin.koujou_id = koujou.id
kouin.name = '浜松浜子'
kouin.save
seihin = kouin.seizou
seihin.save
expect(koujou).to have(1).seihins
end
end
bundle exec rspec spec/models/koujou_spec.rb Rack::File headers parameter replaces cache_control after Rack 1.5. [32m. [0m Finished in 0.52803 seconds [32m1 example, 0 failures [0m Randomized with seed 58919なんとなく気持ち悪いので、下記のように、expectの内部で製造を行った上で、チェックする時点で明示的にDBから値を取得するようにして、差分の確認はbyというマッチャを使うと安定しそうだ。
require 'spec_helper'
describe Koujou do
it '製品製造後の在庫(製品)増加の確認.' do
koujou = Koujou.new
koujou.name = 'テスト'
koujou.save
kouin = Kouin.new
kouin.koujou_id = koujou.id
kouin.name = '浜松浜子'
kouin.save
expect {
seihin = kouin.seizou
seihin.save
}.to change{Koujou.find(koujou.id).seihins.size}.by(1)
end
end
bundle exec rspec spec/models/koujou_spec.rb Rack::File headers parameter replaces cache_control after Rack 1.5. [32m. [0m Finished in 0.59903 seconds [32m1 example, 0 failures [0m Randomized with seed 65120

