rubocop-rspec には MessageExpectation という Cop(Check項目)があります。
これはデフォルトで allow(foo).to receive(:bar)
を推奨し
expect(foo).to receive(:bar)
に対して警告を出します。
この警告がでたときに、「ん?」どういうことなんだろう?
と思い、内容を調べることになりました。
(ほとんど隣の席の人が調べてくれたのだけど)
前段
前提として Given / When / Then について説明します。
Given / When / Then は BDDのテストを書く際に利用される記法です。
基本的な考え方として、テストを3つのセクションに分割して記載します。
Syntax | Description | Example |
---|---|---|
Given | 振る舞いを実行する前の状態を記述します | 事前状態の設定やスタブ |
When | 振る舞いを記述します | テスト対象のメソッドの呼び出し |
Then | 振る舞いの結果を検証します | 検証コード |
これにより各ブロックごとの役割がひと目で分かり、可読性が高まることがメリットと考えられています。
(このスタイルが読みやすいと思わない人にはメリットになりませんね。
例えば、この記法を強制するとワンライナーのテストを気軽に書きにくい、という点もあります)
テストフレームワークによってはこの記法を強制してくるものもあります。
テストフレームワークがこの記法をサポートしていない場合は、
- 拡張ライブラリがあれば使う( 例えば rspec-given )
- 行間を開ける(アナログ対応)
- コメントをつけておく(アナログ対応)
などで Given / When / Then を明示することになるでしょう。
以下は Given / When / Then と rspec-given について私がまとめた記事です。
MessageExpectationの意図は?
ようやく本題です。
rubocop-rspec のプロジェクトで MessageExpectation を作成したときのプルリクエストは以下です。
Add MessageExpectation cop - Pull Request 169 - backus/rubocop-rspec
動作上は
expect(foo).to receive(:bar) # exercise system under test
と
allow(foo).to receive(:bar) # exercise system under test expect(foo).to have_received(:bar)
は同じです。
前者はモック時に :bar
が呼び出されたかどうか?を記述しています。
これは Given / When / Then でいうなら Given の際に Then の内容を記述していることになります。
それに対して後者は Given と Then が明確にわかれています。
この方が好ましいだろう、というのが MessageExpectation が意図するところです。
サンプル
# given # Open3.capture3 をモックします expect(::Open3).to have_received(:capture3).once # when # some logic # then # other verification
$ rubocop Running RuboCop... Inspecting 51 files ..............................................C.... Offenses: ... RSpec/MessageExpectation: Prefer allow for setting message expectations.
expect
ではなく allow
を使うように怒られます。
以下のようにすると警告が消えます。
# given # Open3.capture3 をモックします allow(::Open3).to receive(:capture3) # when # some logic # then expect(::Open3).to have_received(:capture3).once # other verification
ここで問題なのは MessageExpectation という Cop 名や
Prefer allow for setting message expectations.
という警告メッセージからは
本来の意図を把握しにくく、開発時のプルリクエストを見てようやく内容を把握できる
という点です。
未来
MessageExpectation Cop が誤解をうみやすかったせいか、代替手段として MessageSpies Cop が提供されるようです。
2016/12/20時点の CHANGELOG.md をみると master にマージ済みだが未リリースです。
Add MessageSpies cop for enforcing consistent style of either expect(...).to have_received or expect(...).to >receive, intended as a replacement for the MessageExpectation cop.
MessageSpies Cop - Pull Request 224