はじめに
こんにちは。入社して4年目になりました、wai-doi です。
お仕事でRSpecでテストを書いていて、
「引数に特定の値が渡された時だけスタブしたい」
ということがありました。そのときどのように書けばよいか分からなかったので、今回は調べたこととその方法を書きます。
実行環境
- Ruby 3.1.2
- RSpec 3.11.0
サンプルコード
例えば以下の Shopper#buy_fruits
メソッドのテストをしたいとします。単純に配列の ['apple', 'banana', 'orange']
を返します。
class Shopper def buy_fruits(shop) basket = [] basket << shop.sell('apple') basket << shop.sell('banana') basket << shop.sell('orange') basket end end class Shop def sell(fruit) fruit end end
普通に RSpec でテストを書くなら以下のようになります。
describe 'Shopper#buy_fruits' do let(:shop) { Shop.new } it do shopper = Shopper.new expect(shopper.buy_fruits(shop)).to eq ['apple', 'banana', 'orange'] end end
引数に 'banana'
が渡される呼び出しだけをスタブする
ここからが本題です。
このとき、Shop#sell
メソッドの引数に 'banana'
が渡される呼び出しだけをスタブしたいとします。スタブして 'banana'
ではなく nil
を返すようにしてみます。
次のようにテストを書くとうまくできます。
describe 'Shopper#buy_fruits' do let(:shop) { Shop.new } before do allow(shop).to receive(:sell).and_call_original allow(shop).to receive(:sell).with('banana').and_return(nil) end it do shopper = Shopper.new expect(shopper.buy_fruits(shop)).to eq ['apple', nil, 'orange'] end end
引数に 'bannana'
が渡される呼び出しを特定するために with('banana')
を使用しています。
ポイントは、先に and_call_original
を書いて、そのあと with('banana').and_return(nil)
を書く順番です。逆だとうまくいきません。
今回の方法はこちらの Stack Overflow のページを参考にしました。
上記の方法は柔軟には使えない
ただし、上記の方法が使える場合は限られています。以下の場合には使えません。
同じ引数の呼び出しすべてに影響してしまう
例えば次のような、 Shop#sell
の 2 番目と 3 番目の呼び出しが同じ引数の場合です。
class Shopper def buy_fruits(shop) basket = [] basket << shop.sell('apple') basket << shop.sell('banana') basket << shop.sell('banana') basket end end
この場合
allow(shop).to receive(:sell).and_call_original allow(shop).to receive(:sell).with('banana').and_return(nil)
と書いてしまうと、2 番目だけでなく 3 番目の Shop#sell
の呼び出しも nil
を返してしまいます。
例えば 2 番目の呼び出しだけをスタブしたいといったことはできません。
引数を持たないメソッドには使えない
スタブ対象のメソッド(今回の Shop#sell
)が引数をもつ必要があります。引数を持たないメソッドの場合は with
でスタブしたい呼び出しを特定できないため、この方法は使えません。
そのため同じメソッド呼び出しが複数ある場合、この行の呼び出しの時だけスタブしたいといったことはできません。
まとめ
RSpecで「引数に特定の値が渡された時だけスタブしたい」場合について書きました。 and_call_original
と with
でそのスタブしたい呼び出しを特定することで実現できました。
この記事がどなたかのお役に立てば幸いです。
最後に、株式会社永和システムマネジメントでは、Ruby とアジャイルソフトウェア開発を通じてコミュニティと共生しながら成長したいエンジニアを絶賛募集しています。