はじめに
未経験からエンジニアに転職して、3ヶ月経つエンジニアです。
今回は業務で経験した「トランザクションのロールバックをテストする」方法について書きます。
トランザクションとは
ブロック内のすべての処理が正常に行われた場合に保存
エラーが発生した場合は、ロードバック
これだけだとわかりませんよね、、
↓の記事などが参考になるかもしれません!
テストしていく
トランザクションを使っているということは、重要な処理をしている可能性が高いです。
つまり、コードにバグが含まれていると取り返しのつかないことになり得る危険性が高いともいえます。
テストを堅牢にしたくなるのが自然な流れですね。
結論
describe 'transfer' do
# 必要なデータを用意する
let!(:user) { create(:user) }
create(:money, ...)
...
it 'should rollback if raise Error' do
allow(Money).to receive(:update_statuses!).and_raise(StandardError)
expect { XXXXX }.not_to change(Money, :count)
end
end
トランザクション内の処理をスタブして、例外を吐くようにする。例外を検知したトランザクションはロールバックをする。
→ レコードの数に変化がないか検証する(テストする)
class Money < ApplicationRecord
enum status: [:pending, :completed]
def transfer
... # お金を振り込む処理
end
def update_statuses!
... # 振り込みが完了したらステータスを更新する
end
上記のような、
お金を振り込む
→ 振り込みが完了したらステータスを更新する
というプログラムがあるとします。
下記のような感じでトランザクションで囲っているとします。
ActiveRecord::Base.transaction do
Money.transfer
Money.update_statuses!
end
トランザクション内でなんらかの理由でエラーが起きた時に、
ちゃんとロールバックしているかテストしていきます。
describe 'transfer' do
# 必要なデータを用意する
let!(:user) { create(:user) }
create(:money, ...)
...
it 'should rollback if raise Error' do
allow(Money).to receive(:update_statuses!).and_raise(StandardError)
expect { XXXXX }.not_to change(Money, :count)
end
end
ポイントはスタブするという点です。
allow(Money).to receive(:update_statuses!).and_raise(StandardError)
スタブとは、「防ぐ」「止める」というようなイメージで考えてみてください。
ここではトランザクション内の2つ目の処理でスタブしています。
.and_raise(StandardError) することで、例外を吐くようにしました。
これで、ロールバックが発生するので、あとはレコードが増えていないことを検証すればOKです。
expect { XXXXX }.not_to change(Money, :count)
参考