FactoryBotのシーケンス

※振り返れば「だからそうだっていってんじゃん!」と怒られてしまうようなしょうもない事です。

今回記載するのはModel specと呼ばれるモデルに関するテストの備忘録

 

RSpecの基本において参考にしたのは以下

使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 - Qiita

最初はなんだか分からんかったがQiitaの記事を見たり現場Railsを読み、構文を見ながらダラダラと3日目。

理解が先

そんな中で出てきた

FactoryBot.define do
  factory :task do

    sequence(:title, "title_1") # ← これ
    content { "content" }
    status { :todo }
    deadline { 1.week.from_now }
    association :user
  end
end

sequence(シーケンス)

こいつが分からず詰まった。

他のcontent,status,deadline,associationは分かるのよ。コントローラーとモデルの中に書いてあるから。

タイトルはtitleじゃダメなの?ってところ

・前提として


まず、シーケンスとはなんぞや?

thoughtbot/factory_bot

翻訳すると、

グローバルシーケンス 特定の形式のユニークな値(例えば、電子メールアドレス)は、シーケンスを使って生成できます。シーケンスを定義するには、定義ブロックの中でsequenceを呼び出し、シーケンス内の値を生成するにはgenerateを呼び出します。

# 新しいシーケンスの定義
FactoryBot.define do
  sequence :email do |n|
    "person#{n}@example.com"
  end
end

generate :email
# => "person1@example.com"

generate :email
# => "person2@example.com"

# 上記のようにnの部分の数が増えている

以下も参照

FactoryBot(FactoryGirl)チートシート - Qiita

インクリメントとは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

モデルのvalidate内にuniqueness: trueというユニークの記載がある場合はこのシーケンスとやらを使えば良いようだ。

・疑問な点


しかし、解説には

# 元々書いてあったものが
sequence(:title) { |n| "title_#{n}" }

						 ↓ ↓

# この様に変更していた
sequence(:title, "title_1")
#       ↑第一引数  ↑ 第二引数

理由としては

sequenceの第二引数で定義した文字にはループの度にString#nextを末尾の文字に実行される

String#next (Ruby 3.0.0 リファレンスマニュアル)

とリンク先を見るも、いまいち腹落ちせず…

???🤔 ???

こういった記事は見つけたので第二引数の事は分かったが

FactoryBot (旧FactoryGirl) の sequence と .next - Qiita

なぜ "title_1" で認識してくれるのだろうか?

【そういうもの】と思うしかないのか?

という疑問が浮かんだ。

あるslackに質問を投げてみたところ

String#next でnの部分を読んで(呼んで?)くれるコードを教えてもらった

thoughtbot/factory_bot

他の方からも

・「個人的にはブロックを使う方を好む」 ・「個人的には、上の方が何が起こるかを予想しやすくて良い」

などと反応があった。

・解決への光明


そんな中、とある方がconsoleで

~ irb
irb(main):001:0> 'title_1'.next
=> "title_2"

というString#nextの動きを検証してくれたのをきっかけに私の腑に落ちるのが始まる。

先程のString#next (Ruby 3.0.0 リファレンスマニュアル)の動きをやってくれたわけだ。

それを考慮して自分でやってみるとこうなった

Loading development environment (Rails 5.2.3)
[1] pry(main)> 'title_1'.next
=> "title_2"  # titleは数字が変わる
[2] pry(main)> 'user_#{n}@example.com'.next
=> "user_\\#{n}@example.con" # メールでは数字ではなくmがnになった
[3] pry(main)> 'user_{n}@example.com'.next
=> "user_{n}@example.con"
[4] pry(main)> 'user_n@example.com'.next
=> "user_n@example.con"

なので、先程のFactoryBot (旧FactoryGirl) の sequence と .next - Qiitaにある

ブロックを渡さずに第二引数を渡すと、 .next が呼ばれるようになっている

となっていたので、メールを第一引数として.nextを呼んだわけだが、メールはnの部分の数字ではなく末尾の.comのmが.conのnに変わった。

つまり、.nextを呼んでもメールの中の数字が変わることがない(末尾じゃないから)

しかし、title_1を第二引数にしても2に変わる(末尾だから)

答えはすごくシンプルで

title_1の場合は末尾だから数字が(+1)変わる

メールは変えたい部分が末尾じゃないから途中のnの部分は変わらない。だからメールはブロック化して書いている。

なので"title_1"の書き方は

変えたい場所が末尾の場合のみ使える書き方

これで腑に落ちました。

振り返ると**"何でこんなに悩んでたんだろう"**と情けなくなります…笑

🙏