Closed33

RSpec入門・演習2

なななななな

2023.10.16

いよいよRSpecの実際の使い方に入っていく。

なななななな

status(integer)が未入力なのを表すには''なのかnilなのか?
nilは値なし、''は空の値、だから''なのかなあ。

なななななな

早速テストしたけど一個引っかかったな。タイトルが被ったときにinvalidになるかってやつ。
今日はここまでにして、明日そこの解明からやろう。

なななななな

2023.10.17

昨日引っかかったところの解消をする。

    it 'titleが被った場合にuniqueのバリデーションが機能してinvalidになるか' do
      user = User.create(email: 'user@example.com', password: 'password')
      task1 = user.tasks.new(title: 'aaa', content: 'aaa', status: :todo)
      task2 = user.tasks.new(title: 'aaa', content: 'bbb', status: :todo)
      expect(task2).to be_invalid
    end

このケースがエラーになってしまう。

なななななな

う〜ん見直しても原因がわからない…ちゃんとタイトルが重複するようにしてるから、invalidになるはずなんだけどなあ。

なななななな

あ〜なるほど!task1がnewしただけでDBに保存されてないからなんだ!そりゃそうかインスタンス生成するだけならバリデーションも何もないな確かに。createにしないとだめだ。

なななななな

task1 = user.tasks.create(...)としたところ、今度は以下のエラーが出た。

ActiveRecord::RecordNotSaved:
       You cannot call create unless the parent

user = User.create(...) って書いてあるのになんで!?とさすがに意味不明だったのでロボらんてくんに相談したところ、ユーザーの保存がうまくいってない可能性があるのでコンソールでやってみてと言われた。
やってみたら、ActiveRecord::StatementInvalid: Could not find table 'users'というエラーが。確かに保存できてない。
にしても、usersテーブルがない??モデルファイルもあるしデータベーススキーマを見てもできてるっぽいのに???
と思いさらにロボらんてくんに相談すると、rails db:migrateしてみ、とのこと。やってから再度コンソールでユーザーの保存を試みたところ、以下の結果に。

root@46555f1c7713:/sample_app_for_rspec# bin/rails c
Loading development environment (Rails 7.0.4.3)
[1] pry(main)> user = User.create(email: 'user@example.com', password: 'password')
  TRANSACTION (0.0ms)  begin transaction
  User Exists? (0.3ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "user@example.com"], ["LIMIT", 1]]
  TRANSACTION (0.0ms)  rollback transaction
=> #<User:0x0000ffff93dde3c8 id: nil, email: "user@example.com", crypted_password: "$2a$10$Ur0fsnWVZcliSGBnpFuXku6sGWd6rZ.V5BFxH6TP7oCyfFc7W/zLG", salt: "g3-zJizDFWLQcMXzcB5C", created_at: nil, updated_at: nil>

なんか最終的には失敗してるっぽいけど無事テーブルは存在するようになったみたいだ。理屈はわからないけどマイグレーションをする必要があったらしい。
ただ、これ保存できてる?とロボらんくんにきいたところやはりできてないとのこと。user.errors.full_messagesでエラーメッセージ確認してみとアドバイスを貰ったので即実行。すると以下の結果に。

[2] pry(main)> user.errors.full_messages
=> ["Password confirmation can't be blank"]

password_confirmation!!
確かに入力してない!
…これたぶんわざと見本に書かなかったな??まんまと術中にハマってしまった…。

なななななな

時間なので本日はここまで。原因がわかったので、明日修正して先ヘ進む。

なななななな

2023.10.18

昨日判明したエラー原因の解消から。

なななななな

修正して無事テスト成功。現段階のコードをコミットして、次のfactory botの使用に移る。
セットアップと実際のデータ作成を行う。

なななななな

RSpecの設定はrails_helperに書くと。requireで読み込んでるやつだな。

なななななな

userはたぶんこれでOK。問題はtaskだな。アソシエーションで紐づいたオブジェクトの生成をどうやるかってことか。

なななななな

まずはシンプルにuser.create(:task)という書き方を試してみるか。

なななななな

とりあえずテストは知らせてみたところ、以下のエラーが出た。

Failure/Error:
  factory :task do
    sequence(:title) { |n| "task#{n}"}
    content { 'aaa' }
    status { :todo }
  end

FactoryBot::DuplicateDefinitionError:
  Factory already registered: task

taskのfactoryがダブってる??確認してみるか。

なななななな

ん〜??そんな事無いと思うんだけど…。
ひょっとして一つのモデルにつき一つFactoryBot.defineしなきゃいけないとか??

なななななな

試してみたけど変わらんな。どういうことだろう。

なななななな

あ〜〜わかった!FactoryBotのコードを書くためのファイルが別にあった!で、そっちにtaskのfactoryの雛形がすでにある。だからダブったんだ。コードをそっちに移設しよう。

なななななな

移設したら無事ダブりの問題は解決した。ただやっぱりuser.create(:task)って書き方はできないみたいだ。やり方調べよう。まずは公式ドキュメントをあたる。

なななななな

ん〜それっぽい記述は見つけたけどちょっとよくわからない。技術記事に頼るか。

なななななな

あ、なるほどfacory名と関連名が同じ場合は関連名だけ書けばいいんだ。さっきの公式ドキュメントに出てきたコードの意味がわかった。

# ↓これと
FactoryBot.define do
  factory :task do
    content { 'aaa' }
    association :user
  end
end

# ↓これは同じ意味
FactoryBot.define do
  factory :task do
    content { 'aaa' }
    user
  end
end

https://github.com/thoughtbot/factory_bot/blob/main/GETTING_STARTED.md#associations
https://web-begginer-log.com/rspec_factory_bot/

なななななな

ひょっとしてこれわざわざユーザーの生成を別でする必要なかったりする?task生成すると自動で生成されるのでは?試してみるか。

なななななな

お、いけたっぽい。ということでユーザー生成コードは削除して、適宜属性の値をオーバーライドしつつテストコードを修正していこう。

なななななな

「ちゃんとinvalidになるか?」系のテストが全部エラーになった😇
見てみたら

     ActiveRecord::RecordInvalid:
       Validation failed: Title can't be blank

って言われてる。

なななななな

おそらく「invalidになるか?」系は実際に保存(create) しちゃいけないんだな。インスタンス作成までに留めるべき。
修正してもう一度テスト。

なななななな

OK、通った。逆に「ちゃんとvalidになるか」系はどっちのほうがいいんだろうか。とりあえずcreateにしておくか。

なななななな

ここまでの状態をコミットしたので、課題完了にして解答例を確認する。
問題なければそのまま、あれば修正して最終コミット→プルリク。

なななななな

解答例だと異なっている点

  • sequenceの書き方
  • taskにdeadlineを設定してる
  • taskとuserでFactoryBotのファイルを分けてる
  • 必要なところ以外はcreateでなくbuildを使ってる
  • invalidであることが期待されるオブジェクトにはわかりやすい名前をつけている
  • titleがかぶる場合とそうでない場合が並ぶような順番になってる
  • title被り関連についてはチェックするのは後から作成するインスタンスのほうだけでOK

以上の点を修正する。

なななななな

RSpecの基本的な使い方は一度まとめといたほうが良いかも。

このスクラップは2023/10/18にクローズされました