工場の工員が製品を製造するという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
0 件のコメント:
コメントを投稿