質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

Q&A

解決済

1回答

4616閲覧

FactoryBot コールバックを利用したcreate_listについて(everyday rails - rspecによるrailsテスト入門)

Yamken

総合スコア6

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

コールバック

コールバックは他のコードに引数として渡されるコードのことを指します。

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

0グッド

0クリップ

投稿2020/05/28 12:47

everyday rails - rspecによるrailsテスト入門 150ページ目

FactoryBot.define do factory :project do sequence(:name) { |n| "Test Project#{n}"} description {"Sample project for testing purposes"} due_on 1.week.from_now association :owner trait :with_notes do after(:create){ |project| create_list(:note, 5, project: project)} end end end

上記、traitのwith_notesはコールバックによってオブジェクト作成後、

noteを5個作成するまでは理解したのですが、ブロック内の|project| や 

project: projectの意味が、理解出来ません。

|project|は、factory :projectのオブジェクトが入り、project: projectでは、

渡されたオブジェクトをオーバーライドする感じでよろしいのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「Everyday Rails - RSpecによるRailsテスト入門」翻訳者チームの伊藤です。
Everyday Railsを読んでいただき、どうもありがとうございます。
ご質問の件について、できるだけ詳しく説明してみますね。

なお、以下のサンプルコードはEveryday RailsのGitHubリポジトリから抜粋しています。
https://github.com/everydayrails/everydayrails-rspec-2017

ブロック内の|project|

これはFactoryBotによってcreateされたProjectのインスタンスが渡されます。

もしproject = FactoryBot.create :project, :with_notes, name: 'Foo' というコードがあれば、まずFactoryBot.create :project, name: 'Foo'を呼びだしたのと同じようにProjectのインスタンスが作成され、そのインスタンスがtrait :with_notesの中の|project|に渡ってきます。

project: project

これはcreate_listメソッドで作成されるNoteクラスのインスタンスに関連するProjectクラスのインスタンスの指定です。
Noteクラスの定義は次のようになっているので、何らかのProjectに属する必要があります。

ruby

1class Note < ApplicationRecord 2 belongs_to :project 3 belongs_to :user 4 5 # ...

つまり、FactoryBot.create :project, :with_notesをtraitなしで書こうとすると、こんなイメージになります。

project = FactoryBot.create :project 5.times do FactoryBot.create :note, project: project end

なお、ProjectとNoteのファクトリは以下のような定義になっています。

ruby

1FactoryBot.define do 2 factory :project do 3 sequence(:name) { |n| "Project #{n}" } 4 description "A test project." 5 due_on 1.week.from_now 6 association :owner 7 8 trait :with_notes do 9 after(:create) { |project| create_list(:note, 5, project: project) } 10 end 11 12 # ... 13 end 14end 15 16FactoryBot.define do 17 factory :note do 18 message "My important note." 19 association :project 20 user { project.owner } 21 22 trait :with_attachment do 23 attachment { File.new("#{Rails.root}/spec/files/attachment.jpg") } 24 end 25 end 26end

FactoryBotをまったく使わずに書くなら、次のようなイメージになります。

ruby

1owner = User.create!( 2 first_name: "Aaron", 3 last_name: "Sumner", 4 email: "tester1@example.com", 5 password: "dottle-nouveau-pavilion-tights-furze" 6) 7 8project = Project.create!( 9 name: "Project 1", 10 description: "A test project.", 11 due_on: 1.week.from_now, 12 owner: owner 13) 14 155.times do 16 Note.create!( 17 message: "My important note.", 18 project: project, 19 user: project.owner 20 ) 21end

こんな説明でいかがでしょうか?
何か不明な点があればまたご質問ください。

追記

あ、たしかにFactoryBot.を付け忘れてましたね。すいません。コードの方は修正しておきました。
ちなみにspec/rails_helper.rbconfig.include FactoryBot::Syntax::Methodsという設定を入れておくとFactoryBot.が省略できます。(参考
僕はふだんこのスタイルでFactoryBotを使ってます。

コメントに載っていたコードですが、

ruby

1it "userが作成されたらprojectが5個作成される" do 2 project = FactoryBot.create(:user, :with_projects) 3 expect(project.projects.length).to eq 5 4end

は、

ruby

1it "userが作成されたらprojectが5個作成される" do 2 user = FactoryBot.create(:user, :with_projects) 3 expect(user.projects.length).to eq 5 4end

というように変数名をuserにした方がわかりやすいと思いました。
それ以外はOKだと思います????

投稿2020/05/29 00:03

編集2020/05/29 23:05
jnchito

総合スコア357

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Yamken

2020/05/29 10:40

伊藤さん有難うございます!ブロック内のprojectの意味わかりました! 自分なりに変えてテストを作成しました。 テスト it ”userが作成されたらprojectが5個作成される” do project = FactoryBot.create(:user, :with_projects) expect(project.projects.length).to eq 5 end ファクトリー trait :with_projects do after(:create) { |user| create_list( :project, 5, owner: user)} end にするとテストがgreenになり、|user|は作成されたUserインスタンス owner: userは、Projectクラスに関連されたownerという認識です。 ちなみにtrait無しで書く、 project = FactoryBot.create :project 5.times do create :note, project: project end ですが、timesメソッドのcreateの部分が  NoMethodError: undefined method `create' for #<RSpec::ExampleGroups::Project:0x00007fa24b8f1f70> と出ますので、createにFactoryBot.createをつけ project = FactoryBot.create :project 5.times do FactoryBot.create :note, project: project end にすると、エラーが出なくなりました! また、FactoryBotを使わないテストも教えて頂いて有難うございます、 色々な方法試してみます!
Yamken

2020/05/30 01:25

そういう設定があったんですね、早速入れてみます! 確かに変数名userにしたほうがわかりやすいですね、projectが多くてなんだか違和感があったので、すっきりしました、ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問