はじめに
RSpec でテストを書く際に、データの準備方法として let
, let!
, before
があります。
これらの使い分けを明確に理解していないと、意図しない動作をしてしまうことがあります。それぞれの使い方を記事にしてみました!
1. let
と let!
の違い
RSpec では、テストデータを定義するために let
と let!
がよく使われますが、両者には以下の違いがあります。
メソッド | 実行タイミング | 特徴 |
---|---|---|
let |
遅延評価(最初に参照されたとき) | let(:user) { User.create(...) } のように定義しても、テストで user を参照しない限り実行されない |
let! |
即時評価 | let!(:user) { User.create(...) } のように定義すると、テストの前に実行される |
let
の遅延評価
RSpec.describe User, type: :model do let(:user) { User.create(name: "Taro") } it "does not create a user if not accessed" do expect(User.count).to eq(0) # userを参照しないので作成されない end it "creates a user when accessed" do user # ここで user が参照され、作成される expect(User.count).to eq(1) end end
user
は let
によって定義されていますが、明示的に user
を参照しない限り User.create(...)
は実行されません。
let!
の即時評価
RSpec.describe User, type: :model do let!(:user) { User.create(name: "Taro") } it "creates a user before the test runs" do expect(User.count).to eq(1) # user を参照しなくても作成される end end
let!
を使うと、テストの前に User.create(...)
が実行されるため、User.count
は 1 になります。
2. before
と let!
の違い
before
もテストの前に処理を実行できますが、使い方に違いがあります。
before
を使う場合
RSpec.describe User, type: :model do before do User.create(name: "Taro") end it "creates a user before the test runs" do expect(User.count).to eq(1) end end
before
は、指定したブロックの中で直接データを作成できます。
let!
との比較
RSpec.describe User, type: :model do let!(:user) { User.create(name: "Taro") } it "creates a user before the test runs" do expect(User.count).to eq(1) end end
どちらも同じように動作しますが、before
では変数を定義しないため、テスト内で user
を明示的に使用しない場合は let!
の方が適しています。
どちらを使うべきか
before
- 変数を定義せず、処理だけを実行したい場合
- scopeなどのテストで、対象外のデータを作成するときに使用
let!
- 変数を定義しつつ、前もってデータを作成したい場合
- scopeなどのテストで、対象のデータを作成し、明示的に作成したい場合に使用
4. まとめ
let
は遅延評価されるため、不要なデータ作成を防げるが、意図しない未作成の可能性があるlet!
は即時評価されるため、常に事前にデータを用意できるが、不要なデータが作成されることもあるbefore
は変数を定義せず、処理だけを実行するのに適している
以下のgithubで、詳しく説明されており、とてもわかり易かったのでおすすめです!
RSpec スタイルガイド github.com