概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: RSpec - you should avoid let and before blocks to use natural flow
 - 原文公開日: 2018/02/06
 - 著者: Paweł Dąbrowsk
 
Rails tips: RSpecのletブロックやbeforeブロックは基本避けるべき(翻訳)
RSpecの作者はそれなりの理由があってbeforeやletブロックを考案しましたが、本当に必要でない限りこれらの利用は避ける方が賢明です。理由は以下のとおりです。
- テストの本体から
letの定義にジャンプ(またはその逆のジャンプ)することで、テストの自然な流れを妨げることになります。 letを使うとテストが遅くなります。letがシンプルなメソッドに変換されるのは最後の段階だからです(memory usage benchmark)を参照(訳注: リンク切れです)。- specが読みづらくなります。正しいコンテキストを追うにはあちこちにジャンプしなければなりません。
 - spec同士に依存関係が生じます。specは分離するべきです。
 
letブロックやbeforeブロックを使った場合と使わない場合でどう変わるかを、以下のテスト例で考えてみましょう。
require 'spec_helper'
describe Users::NameService do
  describe '#name' do
    let(:user) { instance_double(User, posts?: false) }
    it 'ユーザー名を返す' do
      expect(described_class.new(user).name).to eq(user.name)
    end
    context 'ユーザーにpostsがある場合' do
      before { allow(user).to receive(:posts?).and_return(true) }
      it 'ユーザー名を返す' do
        expect(described_class.new(user).name).to eq("#{user.name} (has posts)")
      end
    end
  end
end
次はbeforeやletを使わない場合です。
describe Users::NameService do
  describe '#name' do
    it 'ユーザー名を返す' do
      user = instance_double(User, posts?: false)
      expect(described_class.new(user).name).to eq(user.name)
    end
    it 'ユーザーにpostsがある場合ユーザー名を返す' do
      user = instance_double(User, posts?: true)
      expect(described_class.new(user).name).to eq("#{user.name} (has posts)")
    end
  end
end
2番目のテストではストーリーを2つに分けてあり、一方を理解するためにitブロックの外まで読む必要はありません。テストコードの重複は若干ありますが、場合によってはDRY(don't repeat yourself)ルールを守るよりも読みやすさの方が重要です。しかも2番目のテストの方が若干高速です(テストが大規模になると実感しやすくなります)が、重要なのは、ロジックや読みやすさを損なわない範囲でできるだけspecが高速になるようにテストを書くことです。さらに、2番目のテストではスタブも1個少なくて済むというメリットもあります。
beforeブロックやletブロックをどうやっても省略できない例はあるものでしょうか?もしご存知でしたらぜひコメント欄までどうぞ。
追記(2018/02/10)
本記事にベンチマークの結果を追記しました。記事を書く前にベンチマークを取っておくべきでした。ベンチマークはgistでご覧いただけます(訳注: リンク切れです)。また、多くの方が私の記事を読み誤っています。私は「いついかなる場合でも」beforeやletを使うなと申し上げたいのではありません。letやbeforeがspecの高速化などに有用な場合は(特に複雑なケースでは)確かにあります。しかし私は常にコードをシンプルに書くようにしており、その方が有用性が高いことに気づいたのです。
RSpec & TDDの電子書籍を無料でダウンロード
もっと稼ぎたい方や会社をさらに発展させたい方へ: テスティングのスキルの重要性にお気づきでしょうか?テストを正しく書き始めることが、唯一のファーストステップです。無料でダウンロードいただける私の書籍『RSpec & Test Driven Developmentの無料ebook』をどうぞお役立てください。
ツイートより
これ読んでたけど、ここに乗ってる let のコード例は使い方が悪いだけでしょ… / Rails tips: RSpecの`let`ブロックや`before`ブロックは基本避けるべき(翻訳) - https://t.co/oWZNIgSH95
— バンビちゃん@実際クソザコナメクジ (@pink_bangbi) July 5, 2018