はじめに
こんにちは。アメリカ在住で独学エンジニアを目指している Taira です。
RSpec でテストを書くときによく出てくるlet
とlet!
。
どちらもテストで使う変数を定義するためのヘルパーですが、動き方が少し違います。
この記事では、それぞれの特徴と使い分け方を、コード例を交えて解説します。
1. let
とは?
let
は**遅延評価(lazy evaluation)**される変数を定義します。
つまり、初めて呼び出されたときに実行されるという動きです。
RSpec.describe User do
let(:user) { User.create(name: "Taro") }
it "最初に呼び出したタイミングで実行される" do
# この時点ではUserはまだ作られていない
expect(User.count).to eq(0)
user # ← 初めて呼び出したタイミングでUserが作成される
expect(User.count).to eq(1)
end
end
✅ ポイント
- 実際に使われなければ、定義部分は実行されない
- 無駄なデータ作成を避けられる(テスト高速化につながる)
2. let!
とは?
let!
は**即時評価(eager evaluation)**されます。
テストが始まる前に必ず実行されるため、呼び出さなくても変数が作られます。
RSpec.describe User do
let!(:user) { User.create(name: "Taro") }
it "テスト開始前にすでに作成されている" do
# let! は呼び出さなくても事前に実行される
expect(User.count).to eq(1)
end
end
✅ ポイント
- 呼び出さなくても必ず実行される
- 事前にデータを用意しておきたいときに便利
3. 違いをまとめると
特徴 | let | let! |
---|---|---|
実行タイミング | 初回呼び出し時 | 各テストの実行前 |
呼び出さない場合 | 実行されない | 必ず実行される |
主な用途 | 必要なときだけデータを用意 | 前提条件として必ずデータを用意 |
4. 使い分けの目安
let
を使うべきとき
- 不要なデータ作成を避けたいとき
- テストの中で呼ばれるかわからないが、とりあえず変数を用意しておきたいとき
let(:article) { create(:article) } # 呼ばれない限りDBにINSERTされない
let!
を使うべきとき
- テスト開始前に必ずデータが必要なとき
-
before
ブロックでセットアップする代わりに使うとスッキリする場合
let!(:article) { create(:article) } # 各itの前に必ず作られる
5. 実務でよくあるハマりポイント
❌「あれ、データがない…」
let(:user) { create(:user) }
it "ユーザー数を確認する" do
expect(User.count).to eq(1) # ← 0になる(userを呼び出してない)
end
➡ 解決策:必ず事前に必要ならlet!
にするか、user
を呼び出す。
6. まとめ
-
let
→ 初回呼び出し時に実行(遅延評価) -
let!
→ テスト開始前に実行(即時評価) - パフォーマンスを意識して
let
を使い、**必須データはlet!
**で作成する
💡 基本はlet
、本当に必要なときだけlet!
にするとテストが速く、意図しないデータ作成も防げます。