問題
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) }
おわり