Rails Diary

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

テストの書き方における疑問点(メモ)

テストの書き方で疑問だった箇所とその理由

describe '検索機能' do
      let(:article_with_author) { create(:article, :with_author, author_name: '田中') }
      let(:article_with_another_author) { create(:article, :with_author, author_name: '佐藤') }
# 中略

      it '著者名で絞り込み検索ができること' do
        article_with_author # ①
        article_with_another_author
        visit admin_articles_path
        select '田中', from: 'q[author_id]'  #②
        click_button '検索'
        expect(page).to have_content(article_with_author.title), '著者名での検索ができていません'
        expect(page).not_to have_content(article_with_another_author.title), '著者名での絞り込みができていません'
      end
  • 指定した著者名で検索ができているのかテストしたいので、著者名田中の記事と著者名佐藤の記事をそれぞれletで作成している

① article_with_authorと記載する理由は?

describe直下に記載したletをlet!にしてしまえば、わざわざitの下にarticle_with_author,article_with_another_authorと記載しなくても済むのに、なぜ上記のようにしているのか。

  • 結論let!でも構わない
  • 全部let!って良いじゃんと思っていたけれど、let!でexampleを個別実行した場合、そのテストケースに必要ないデータまで作成されてしまいテスト実行時間が若干遅くなる
  • 今は関係なくてもアプリの拡大があれば、パフォーマンスも考慮しなくてはならないので頭の片隅に入れておく
  • ただテストにおいて最も重視されるべきなのは、コードの記述量でも実行時間でもなく「可読性」!!

Clean Test Code Revised - Speaker Deck

② select '田中', from: 'q[author_id]'の箇所

Capybaraにおけるシミュレーションでselect '田中', from: 'q[author_name]'と記載すべきなのか。(select article_with_author.author_nameと書いた方が今までの学習に近い)

  • E2E(システムスペックのようなユーザーの操作をシミュレーションする)テストにおいて、「ユーザーの操作を忠実に再現すべき」という考えがあり、その点で言えばfill_in name, with: '田中'の方が好ましいのではないかという考え方
    →これに関しては書き手の考え方によるので、厳格にこうしなければならない!!というものはない。

  • あとは単純に、何を選択するのか、入力するのか明記してあった方がリーダブルである
    →確かに圧倒的にaritcle_with_author.author_nameみたいに長々書くより読みやすいと思った。

www.youtube.com

FactoryBotにおけるcreateとbuildの使い所

trait :with_tag do
  transient do
    sequence(:name, "test_tag_name_1")
    sequence(:slug, "test_tag_slug_1")
  end

  after(:build) do |article, evaluator|
    article.tags << build(:tag, name: evaluator.tag_name, slug: evaluator.tag_slug) # ここ
  end
end

trait :with_sentence do
  transient do
    sequence(:sentence_body, 'test_body_1')
  end

  after(:build) do |article, evaluator|
    article.sentences << create(:sentence, body: evaluator.sentence_body) # ここ
  end
end

コールバックのブロック内、シャープの箇所でbuildとcreateと追加分けている理由は?

article_blockモデルのバリデーションをチェック

# app/models/aritcle_block.rb
class ArticleBlock < ApplicationRecord
  belongs_to :article
  belongs_to :blockable, polymorphic: true, dependent: :destroy # ここ

  with_options on: %i[create update] do
    validates :blockable_type, presence: true 
    validates :blockable_id, presence: true #これ
    validates :level, presence: true, uniqueness: { scope: :article_id }
  end
  • belongs_to :blockable, polymorphic: true, dependent: :destroyの記述があるので、blockable_typeとblockable_idのバリデーションは要らない可能性アリ
  • 悪さ(?)をしているのはvalidates :blockable_id, presence: trueの一行らしい
  • blockable_idはDB保存時に自動で振られる値のため、DBに保存しないbuildではnilになり、バリデーションに引っ掛かってしまう。このため、createを用いてテストに通るようにする

参考サイト

Rails tips: RSpecテストの高速化/リファクタリングに役立つ4つの手法(翻訳)|TechRacho by BPS株式会社

GitHub - willnet/rspec-style-guide: 可読性の高いテストコードを書くためのお作法集