「let・let!・before」の違いと実行順序(RSpec)

f:id:ryoutaku_jo:20190911000621p:plain

【結論】

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!」の実行順の違い

なおletlet!については、それぞれの実行順に注意する必要があります。

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が生成されるので、正常にテストが通ります。

参考情報

RSpecのletを使うのはどんなときか?(翻訳) - Qiita

RSpec の letとlet!とbeforeの挙動と実行される順番 - Qiita

《今日の学習進捗(3年以内に10000時間に向けて)》

本日実装した機能のメソッドが100行以上に及ぶ処理になってしまい、かなり見通しが悪い。
とりあえずメソッドを分割して対応するが、そもそもの設計や仕様を見直す必要があるかもしれない。
設計については、良いアイデアが現状思い付かないので、先輩の助力を仰ごうと思う。

なお、社内でデザインパターンの勉強会が行われたが、他の人達の設計に対する考え方に知る良い機会になり、非常に有意義だった。
まだまだ設計や原則全般に対する理解が乏しいため、消化しきれていない部分が多々あるが、関連書籍を参照して理解を深めていきたい。

学習開始からの期間 :277日
今日までの合計時間:2592h
一日あたりの平均学習時間:9.4h
今日までに到達すべき目標時間:2530h
目標との解離:62h
「10,000時間」まで、

残り・・・「7408時間!」