RSpecメモ(1)
RSpecを書く際のポイントメモ
■ エラーチェック
task = user.tasks.new(title: “”, content: “aaa”, status: :todo) expect(task.valid?).to be(false) expect(task.errors).not_to be_empty expect(task.errors[:status]).to eq [“can’t be blank”]
- errorが空出ないことの確認だけではなく、エラー文に想定する文言が含まれているかもテストする
■ タイトルの重複チェック
it ‘is invalid with a duplicate title’ do user = User.create(email: “user@example.com”, password: “password”) task1 = user.tasks.create(title: “aaa”, content: “aaa”, status: :todo) task2→task_with_duplicated_title = user.tasks.build(title: “aaa”, content: “aaa”, status: :todo) expect(task2.valid?).to be(false) expect(task2.errors[:title]).to eq [“has already been taken”] end
- 一つ目のタスクを適当に作成し、二つ目を一つ目と同じtask.titleにすることでtitleの重複状態を作る
- 一つ目をcreateで作成し、二つ目をbuildで未saveにしているのは二つ目がtitleの重複によってsaveできないことを検証するテストだから
- 基準(?)となる一つ目のtaskオブジェクトは他で使わないので変数に入れる必要はない
- タスクの変数をtaskやtask1と書くだけだとイメージしづらいので、task_without_titleやtask_with_duplicated_titleの様に具体的な名前にすると良い
■ be_validやbe_invaidマッチャを使う
expect(task.valid?).to be(false) # 書き換え expect(task).to be_valid
expect(task.invalid?).to be(true) expect(task).to be_invalid # 良いか悪いかはさておき(分からない)、こっちでも通った expect(task).not_to be_valid
FactoryBotについて
# spec/factories/users.rb FactoryBot.define do factory :user do sequence(:email) { |n| "user_#{n}@example.com" } password { "password" } password_confirmation { "password" } end end
# spec/factories/tasks.rb FactoryBot.define do factory :task do sequence(:title, "title_1") content { "content" } status { :todo } deadline { 1.week.from_now } association :user end end
- contentとstatusは一意である必要がないので、そのまま打ち込む
associationでuserとの関連を記載することで、データ作成時にuserを記載する必要がなくなる(後述)。
上記のsequenceの書き方について
連番データを作成することができる。ユニークな値を作りたい場合に用いる。
sequence(:title) { |n| "title_#{n}" }
の書き方の方がどういう動きか見えやすい気がするが…
sequence(:title, "title_1")
は何が起きているのか分からない。
二つ目の書き方では何が起こっているのか。
こちらのサイトによると、「ブロックを渡さずに第二引数を渡すと、.next(Rubyメソッド)
が呼ばれるようになっている」ので
このように異なる連番のtitleを作り出せる(ちなみにテストをカタカナで書くと上手くいかない)
emailのsequenceは変えたい番号が文字列の途中に挟まっているので、nextメソッドで上手く変更を入れることができない。ブロックを使う。
FactoryBotに記載したデータを使う
user = User.create(email: "test@example.com", password: "password") task = user.tasks.build(title: "test1", content: "test1") expect(task).to be_valid
みたいな形でexample(テストの1単位)を作ってきたが、FactoryBotを追加することで、記載が楽になる。
↓
確かにごちゃごちゃしていない
user = FactoryBot.create(:user) task = FactoryBot.build(:task, user: user)
↓
さらに、先ほどspec/factories/tasks.rbにassociation :user
というuserとの関連を記載したが、アレによってuserの記載が省略できる
task = FactoryBot.build(:task)
一層シンプルに!
さらに…
↓
rails_helper.rb内のconfigブロック内に以下を記載
# spec/rails_helper.rb config.include FactoryBot::Syntax::Methods
これにより、データ作成の先頭にあったFactoryBotをも省略できる
task = build(:task)
もはや原型がない。DRYすぎて返って分かりづらい気もするけど、テスト数が増えてくればこの簡素な感じが役に立つのかなと思う。や
所感
こうやって順を追って、「こういう理由だからこの記載を省略できる」と分かりやすいのかもしれない。最初から洗練された簡素なコードを見てしまうと、情報量の少なさにどうしたら良いのか分からなくなった。
書き方に全く馴染みがなかったのもあるけれど、いきなりFactoryBotでテスト用のダミーデータ作成、RSpecのテスト構文を色々試してみるだと混乱するだけなので、まずはFactoryBotを使わず、慣れないマッチャを使わず、RSpecのDRY回避をせず、少しずつ試してみれば良かったなと思います。
よく分からないままlet(:user) { emai: "", password: "password"}みたいなことをしていたので、与えられた条件の中で上手く工夫することが苦手だなぁと・・・。 あとは本来アプリ上の不具合を予防するためにテストを書くはずなのに、そのテスト自体でエラーが出ちゃう状態に苦笑いでした笑
参考にしたサイト
RSpec
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 - Qiita
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」 - Qiita
後々参考にしたいサイト
使えるRSpec入門・その3「ゼロからわかるモック(mock)を使ったテストの書き方」 - Qiita
FactoryBot
FactoryBot(FactoryGirl)チートシート - Qiita
Factorybotを使ったテストデータの作成方法 - Qiita