factory_girl_railsで生成したオブジェクトを破壊的メソッドで変更する場合の注意点
factory_girl_railsを使ってテストデータを生成し、
そのデータを直接変更した場合にはまったので紹介しておきます。
Factoryで生成したオブジェクトを破壊的メソッドを使って変更すると、
別のテストで再度生成する場合に、変更された値で生成されるということです。(つまりFactoryの型を破壊してしまうということ)
具体的な例で説明します。
一つのテストデータを使って、一部分だけ変更したデータを複数作成したい場合があります。
例えば、レポートNoだけをインクリメントして10件登録するような場合を考えます。
ベースとなるテストデータは次の通り。
test/factories/reports.rb
FactoryGirl.define do
factory :report_test1, class: :Report do
report_no 'R00001'
title 'report title'
content 'report content'
end
end
テストコードは次の通り。
(レポートNo確認用のログも含めています)
レポートを10件生成して一覧のテストを行い、
レポートを1件生成して表示のテスト行う場合です。
test/controller/reports_controller_test.rb
require 'test_helper'
describe ReportsController do
describe "index" do
before do
report_no = FactoryGirl.build(:report_test1).report_no
10.times do
report = FactoryGirl.build(:report_test1)
report.report_no = report_no.succ! #破壊的メソッドでインクリメント
puts "[index]report no:#{report.report_no}"
report.save
end
end
it "10件のデータを取得できる" do
get :index
assert_equal 10, assigns(:reports).count
end
end
describe "show" do
before do
report = FactoryGirl.create(:report_test1)
puts "[show]report no:#{report.report_no}"
end
it "指定したレポートを取得できる" do
get :show, id: 1
assert_equal 'R00001', assigns(:report).report_no
end
end
end
結果は次の通りです。
showのテストで登録した1件のレポートNoが期待した値と異なっていました。
indexのテストで更新したレポートNoが残った形となっています。
$ bundle exec rake test
Run options: --seed 59117
# Running tests:
[index]report no:R00002
[index]report no:R00003
[index]report no:R00004
[index]report no:R00005
[index]report no:R00006
[index]report no:R00007
[index]report no:R00008
[index]report no:R00009
[index]report no:R00010
[index]report no:R00011
.[show]report no:R00011 // ここはR00001を期待していた
F
Finished tests in 0.097491s, 20.5147 tests/s, 20.5147 assertions/s.
1) Failure:
ReportsController::show#test_0001_指定したレポートを取得できる [/Users/xxx/test_app/test/controllers/reports_controller_test.rb:29]:
Expected: "R00001"
Actual: "R00011"
2 tests, 2 assertions, 1 failures, 0 errors, 0 skips
Factoryのベースとなるデータは使い回されていると考えると納得できます。
そうとわかれば対策は次の通りです。
最初のレポートNoを取得する部分を変更します。
report_no = FactoryGirl.build(:report_test1).report_no.dup
これで期待した結果となりました。
$ be rake test
Run options: --seed 21061
# Running tests:
[index]report no:R00002
[index]report no:R00003
[index]report no:R00004
[index]report no:R00005
[index]report no:R00006
[index]report no:R00007
[index]report no:R00008
[index]report no:R00009
[index]report no:R00010
[index]report no:R00011
.[show]report no:R00001
.
Finished tests in 0.089792s, 22.2737 tests/s, 22.2737 assertions/s.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips