「let・let!・before」の違いと実行順序(RSpec)
【結論】
・let
/let!
はインスタンス変数を定義する際、before
より分かりやすく記述できる
・before
はインスタンス変数の定義以外(メソッドの実行など)でも利用することができる
・let
は変数が初めて使用された時に評価(遅延評価)され、let!
とbefore
はブロック宣言時に評価される。
【目次】
【本題】
let・let!・beforeの役割について
let・let!・before
は、いずれもRSpecにおいて、各exampleで共通の処理を定義することができます。
これによりそれぞれのテストに同じ記述をする手間が省け、DRYな状態をキープできます。
なお、これらは意識していないと、どれも同じ動きをしている様に見えますが、実際には挙動や使用する場面が異なります。
今回はそれをまとめます。
「let・let!」はインスタンス変数の定義に用いる
各exampleで共通利用するインスタンス変数を定義する際、before
を利用すると、以下の様なコードになります。
before do @user = create(:user, name: 'alice') @post = create(:post, title: 'test') end
これをlet
で書き換えると以下の様になります。
let(:user) { create(:user, name: 'alice') } let(:post) { create(:post, title: 'test') }
let
の方がやや見通しが良くなったと思います。
この事からインスタンス変数の定義を共通化する際には、let・let!
を使用するのが一般的の様です。
「before」はメソッドを実行する際に用いる
ではbefore
はどの様な場面で用いるかというと、インスタンス変数の定義以外で利用します。
例えば、以下の様にサインインの処理を記述すれば、各exampleが実行される前に共通して処理が走ります。
before { sign_in(current_user) }
let・let!
では、この様な使い方は出来ないので、before
が活躍します。
「let」と「let!」の実行順の違い
なおlet
とlet!
については、それぞれの実行順に注意する必要があります。
let
は変数が初めて使用された時に評価(遅延評価)されます。
let!
はブロック宣言時に評価される。
例えば、以下のコードは実行順の関係で、正常に完了しません。
let(:post) { create(:post, title: 'test') } it 'ポストが取得できること' do expect(Post.first).to eq post end
Post.first
が呼び出された時点では、postが生成されていない(呼び出されていない為)ことが原因です。
これを回避するにはlet!
を用います。
let!(:post) { create(:post, title: 'test') } it 'ポストが取得できること' do expect(Post.first).to eq post end
これにより、Post.first
が呼び出された時点では、postが生成されるので、正常にテストが通ります。