同一のパラメータでメソッド呼び出しを行っている。しかし、呼び出した回数によって返却値を変更したい。
そのようなユースケースを満たすために素振りしました。
環境
- 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でのシェアや、今後も情報発信しますのでフォローよろしくお願いします。