Rails tips: RSpecでコード実行回数をスマートに数える(翻訳)
RSpecでは、指定のメソッドが呼び出される回数をexpect
で指定できます。その方法を説明する前に、呼び出し回数を知ることの重要性についてお話します。これはコードの振る舞いを制御するためのものです。説明のため、わざと誤ったコードを書いてみます。
class SomeClass
def something
@something = service.call
end
end
これのどこが誤りかおわかりでしょうか。ここではメモ化によって、SomeClass#something
が呼ばれるたびにservice.call
が2回以上呼ばれることのないようにしたいと考えています。しかしメモ化の||=
演算子ではなく=
演算子が使われているため、期待どおりメモ化されていません。
このコードに対して次のようなテストを書くとします。
expect(some_class_instance).to have_received(:something)
このテストでは、メモ化が行われていないという誤りを検出できません。上のようなシンプルな例ならともかく、たとえばservice.call
が外部APIを呼び出していて、限りあるリソースを食いつぶしているとしたら非常にまずいことになります。
これを回避するには、メソッド呼び出しが何回行われるべきかを指定します。
expect(some_class_instance).to have_received(:something).once
このexpectationを用いることで誤りが検出されます。しかしTDD(テスト駆動開発)を使っていたとしたら、このテストは決して作られることはないでしょう。このことは、TDDを利用することに加えて、expectationを詳しく書くことも重要であることを示しています。
RSpec構文で2回以上のメッセージ呼び出しのexpectationを書く
コードが2回以上呼び出されたかどうかをテストしたい場合や、少なくとも2回または3回以上呼び出されたかどうかをテストしたい場合はどのように書けばよいでしょうか?RSpecの文法ではこれらも指定できます。
- メッセージが
n
回呼び出されるexpectation:
expect(some_class_instance).to have_received(:something).once
expect(some_class_instance).to have_received(:something).twice
expect(some_class_instance).to have_received(:something).exactly(3).times
- メッセージが
n
回以上呼び出されるexpectation:
expect(some_class_instance).to have_received(:something).at_least(:once)
expect(some_class_instance).to have_received(:something).at_least(:twice)
expect(some_class_instance).to have_received(:something).at_last(3).times
- メッセージの呼び出しが
n
回以下のexpectation:
expect(some_class_instance).to have_received(:something).at_most(:once)
expect(some_class_instance).to have_received(:something).at_most(:twice)
expect(some_class_instance).to have_received(:something).at_most(3).times
概要
原著者の許諾を得て翻訳・公開いたします。
参考: RSpec 3.12ドキュメント Class:
RSpec::Mocks::MessageExpectation