問題
User has_many Cup(s)
Cup belongs_to User
という関連を作成したあと、以下のようなFactoryを作成。
users.rb
FactoryBot.define do
factory :user do
name { "user" }
email { "user@example.com" }
password { "test123" }
password_confirmation { "test123" }
end
end
cups.rb
FactoryBot.define do
factory :cup do
user
comment { "This is my cup" }
end
end
cups.rbのfactory :cupの中に、user
を記載している。
この状態で、テストに以下のような記載をすると
バリデーションに失敗しました
のエラーが発生してしまう。
describe "GET #index" do
context "with user" do
let(:user) { create(:user) }
let(:cup) { create(:cup) }
before do
sign_in(user)
end
it "gets response 200" do
expect(response.status).to eq 200
end
# 以下itから始まるテストが続く
# ここでは省略する
end
end
[1] pry(<RSpec::ExampleGroups::UserCupsResponse::POSTCreate::WithUser>)> cup
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Emailはすでに存在します, Nameはすでに存在します
from /Users/reiforu/dev/cofapp/vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.3/lib/active_record/validations.rb:80:in `raise_validation_error'
name
とerror
はuser.rbのカラムで、models/user.rb
にて
どちらもuniqueness: true
を付与している。
私の解決法
let(:cup) { create(:cup) }
でバリデーションエラーが出たってことは、
user
が既に作られているということ?
けど、cup.rb
にはuser
を記載して関連づけたし、このuser
は存在しなければならない。
だから、let(:user)
も記載しなくちゃいけないよな。。
あ、でもここでlet(:user)
して既にuser
ができちゃってるのがダメなのかも。。
試しにlet(:user)
消してみよう。
describe "GET #index" do
context "with user" do
let(:cup) { create(:cup, user: user) }
1] pry(<RSpec::ExampleGroups::UserCupsResponse::POSTCreate::WithUser>)> cup
+----+-------------------------+-------------------------+----------------+---------+
| id | created_at | updated_at | comment | user_id |
+----+-------------------------+-------------------------+----------------+---------+
| 3 | 2019-05-12 05:37:28 UTC | 2019-05-12 05:37:28 UTC | This is my cup | 1 |
+----+-------------------------+-------------------------+----------------+---------+
1 row in set
登録できた!
てことは、cup作成時にuserというfactoryを【新たに】作成する
ってことか。
cup.rb
にuser
を記載して関連付けを定義したから、
そのuser
を別途let(:user)
として呼び出さなきゃいけないと思ってた。。
けど
これだとbefore〜do
内のsign_in(user)
のuser
がnil
になってログインできない。。
よし、じゃあ試しにlet(:user)
を記載した上で、
let(:cup)
にuser
を付与してみよう。
describe "GET #index" do
context "with user" do
let(:user) { create(:user) }
let(:cup) { create(:cup, user: user) }
[1] pry(<RSpec::ExampleGroups::UserCupsResponse::POSTCreate::WithUser>)> cup
+----+-------------------------+-------------------------+----------------+---------+
| id | created_at | updated_at | comment | user_id |
+----+-------------------------+-------------------------+----------------+---------+
| 3 | 2019-05-12 05:21:12 UTC | 2019-05-12 05:21:12 UTC | This is my cup | 1 |
+----+-------------------------+-------------------------+----------------+---------+
1 row in set
登録できたー!そして、sign_in(user)
もうまく実行できた。
まとめ
FactoryBot.define do
factory :cup do
user
comment { "This is my cup" }
end
end
factory
に上記user
のような定義をした場合、
● cup
インスタンスの作成時に新たにuser
を作成し、関連づける。
→この際、let(:user)
などを定義していて既に同じuser
がいるとバリデーションエラーが発生する。
●しかし、sign_in(user)
のように、引数にどうしてもuser
が必要な際は、
テスト内でlet(:user)を作成し、そのuser
をcup
に関連付ける。
# こんな感じ
let(:user) { create(:user) }
let(:cup) { create(:cup, user: user) }
おわり