Rails Diary

プログラミングの学習記録です。

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は一意である必要がないので、そのまま打ち込む
  • 今現在から一週間の間でランダムに値を作ってくれているらしい
    https://i.gyazo.com/bb59c984c46158a9574a26ac25b34aa7.png

  • associationでuserとの関連を記載することで、データ作成時にuserを記載する必要がなくなる(後述)。

上記のsequenceの書き方について

連番データを作成することができる。ユニークな値を作りたい場合に用いる。

sequence(:title) { |n| "title_#{n}" }

の書き方の方がどういう動きか見えやすい気がするが…

sequence(:title, "title_1")

は何が起きているのか分からない。
二つ目の書き方では何が起こっているのか。

こちらのサイトによると、「ブロックを渡さずに第二引数を渡すと、.next(Rubyメソッド)が呼ばれるようになっている」ので

https://i.gyazo.com/7b3ea94000ab4c5a5eb8998b201d985c.png

このように異なる連番の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

Sequence

Rspec,FactoryBotのsequence - Qiita

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