同一のパラメータでメソッド呼び出しを行っている。しかし、呼び出した回数によって返却値を変更したい。
そのようなユースケースを満たすために素振りしました。
環境
- Ruby
- 3.0.2p107
- Rails
- 6.0.3.7
- RSpec
ユースケース
時刻は常に、Time.now
で取得している。時刻を元にして主キーを生成しているが、テスト時に取得する時刻が常に同一だと主キー制約違反が発生してテストができない。
ですので、Time.now
の呼び出し回数によって返却する時刻を変更することで、課題を解決したい。
対応
allow
メソッドでクラスをモックにします。receive
で対象メソッドを指定しますand_return
で複数のパラメータを指定します
and_return
のパラメータが、呼び出す回数とマッピングされます。
第1パラメータが1回目の呼び出しでの返却値、第2パラメータが2回目の呼び出しでの返却値となります。指定したパラメータ数よりも多くの呼び出しをしている場合、最後に指定していたパラメータを常に返却します。
describe "呼び出す回数ごとに処理する内容を変更する" do let(:_20200101) { Time.new(2020, 1, 1, 0, 0, 0) } let(:_20210101) { Time.new(2021, 1, 1, 0, 0, 0) } before do allow(Time).to receive(:now) .and_return(_20200101, _20210101) end it "1回目、2回目以降は引数の違いで異なる値が返却される。3回目と4回目は一致していること" do expect(Time.now).not_to eq(Time.now) expect(Time.now).to eq(Time.now) end end
なお、このやり方はリファクタリングをひどく難しくします。リファクタリングのために処理順序を変えるとテストがエラーになってしまう可能性が高いです。テストが壊れやすくなるので、個人的にはオススメしません。Productコード自体は問題ないのも、また厄介です。
describe "呼び出す回数ごとに処理する内容を変更する" do let(:_20200101) { Time.new(2020, 1, 1, 0, 0, 0) } let(:_20210101) { Time.new(2021, 1, 1, 0, 0, 0) } before do allow(Time).to receive(:now) .and_return(_20200101, _20210101) end it "1回目、2回目以降は引数の違いで異なる値が返却される。3回目と4回目は一致していること" do # 1回事前にTime.nowを読んでたりすると、エラーになる Time.now expect(Time.now).not_to eq(Time.now) expect(Time.now).to eq(Time.now) end end
ソースコード
終わりに
正直、困っていたわけではありませんが、本業でよくこのケースでハマったのでRubyでも同様の事象が起こらないか調べました。
この記事がお役に立ちましたら、各種SNSでのシェアや、今後も情報発信しますのでフォローよろしくお願いします。