RSpec のテストで let! で即座に変数作成、let で遅延評価との違い

今日も作成中の Rails4アプリで RSpec テストを作っては実装を行なっていたのですけど、はまった点が一箇所あった。

user has_many comments.
comment belongs_to user.
DB の comments テーブルには、user_id という外部キーがある。

以上の関係になっているモデルでのテストにおいて、@user.comments(@user に属している comments)で、comment が作成日時の新しい順に並んでいることを保証するテストを書きました。

スポンサーリンク

バグのあるテストコード spec/models/user_spec.rb

最初に、user_spec.rb に書いたコード。実はこのコードにはバグが含まれています。

spec/models/user_spec.rb

テストが通らないことを確認後、実装を行いました(実装のコードは省略)。その後、再び以下のとおりテスト実行。

正しく実装を終えたはずなのに、テストを何度繰り返してもテストが通りません。

let は遅延評価で let! は即座に評価、両者の違い

こういう場合は、テストコードにミスがある場合も多いというのを学習したので、調べたところ…

let で作った変数は lazy(遅延評価)であり、変数が参照されたときにはじめて初期化される。つまり最後の、eq を使って newer_comment, older_comment が出現した時点で初めて評価される。だから、@user.comments はその時点で空っぽのままであり、どうやってもテストを通るはずがない…と。

let が遅延評価であるのに対して、let!(letバン)は、各々のテストが実行される前に評価され、即座に変数が初期化されます。

@user.comments が空ではテストにならないので、newer_comment, older_comment は、遅延評価(let)ではなく、即座(let!)に作成する必要があります。let! を使うことで、最後の eq を使ったテストでの、@user.comments にちゃんと作成した newer_comment, older_comment が格納されることになる。

正しいテストコード spec/models/user_spec.rb

let を let! に変更して、以下のとおり正しいテストコードに修正しました。

spec/models/user_spec.rb

ということで、let を let! に変更したところ、正しくテストが通りました。たったこれだけの違いですけどはまりました。解決できて良かったです^^。テスト駆動開発はテストコード側にバグがある場合がなかなか気づきにくい。実装側にミスがある時にはすごく大助かりなのですけど。

スポンサーリンク
私は Rails のテストフレームワークには RSpec を使っています。サーバーのテスト用に Serverspec もおすすめです。
スポンサーリンク
 
Twitterを使っていますのでフォローお願いたします!ブログの更新情報もつぶやいてます^^
(英語学習用)

Leave Your Message!