Introduction
Railsでhas_manyやbelongs_toなどのアソシエーションのあるモデルをfactory_botで生成する方法のメモ。
いままでずっと、
FactoryBot.define do
factory :author do
name { "Author Name" }
end
factory :book do
author_id { 1 }
title { "Book Title" }
end
end
# ~~~ #
background do
@author = create(:author)
@book1 = create(:book, author_id: @author.id, title: "book 1")
@book2 = create(:book, author_id: @author.id, title: "book 2")
@book3 = create(:book, author_id: @author.id, title: "book 3")
@book4 = create(:book, author_id: @author.id, title: "book 4")
@book5 = create(:book, author_id: @author.id, title: "book 5")
end
# ~~~ #
みたいな書き方をしてしまっていたのですが、それぞれの項目は連番とかでよいテストではもっといい方法がありました...
前提
Author has_many Books (Authorモデルは0以上のBookモデルを子に持つ)
Book belongs_to Author (Bookモデルは1つのAuthorモデルを親に持つ)
| Author |
|---|
| id:integer |
| name:string |
| Book |
|---|
| id:integer |
| author_id:integer |
| title:string |
ちなみにselenium+rspec+capybaraでsystem testをするのを想定してます。
Rails on Dockerにて、Headless ChromeでSystem Testをやってみた。 - Qiita
また、使用しているfactory_botのversionは5.0.2です。
Solusion
FactoryBot.define do
factory :author do
name { "Author Name" }
factory :author_with_books do
transient do
books_count { 5 }
end
after(:create) do |author, evaluator|
create_list(:book, evaluator.books_count, author: author)
end
end
end
factory :book do
title { generate(:book_title) }
end
sequence :book_title do |n|
"Book Title #{n}"
end
end
これで
-
create(:author)でBookを持たないAuthor -
create(:author_with_books)でBookを5つ持つAuthor
が作れるようになりました。create(:author_with_books)でできるBookの数は7行目のbooks_countで変更できます。
また、Bookのtitleをgenerate(:book_title)としています。これは19行目のsequence :book_titleを呼び出していますが、sequenceは呼び出されるたびにnがカウントアップするため、Book Title 1, Book Title 2, ・・・、といったtitleのBookオブジェクトが5つできることになります。
# ~~~ #
background do
@author = create(:author_with_books)
end
# ~~~ #
テストシナリオファイル側では1行で1つのAuthorオブジェクトとそれに紐づく5つのBookオブジェクトを扱えるようになります。Bookオブジェクトは、@author.books[i]の形でアクセスすることができます。
Conclusion
テストシナリオファイルがかなりスッキリしました。
Getting Started見る限り、まだまだ知らない便利な書き方がありそう。ちゃんと読まねば。