2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Rspec】モデルからのレコード更新時にテストで値が更新されない時の解決法

Last updated at Posted at 2023-03-05

要約

モデルの更新処理のテストが失敗する場合は、changeメソッドをブロックで書くことで更新後の値と比較されるようになってうまくいくかも?

it { expect { subject }.to change { some_model.reload.changed, true } }

課題

このようなchangedというカラムを持つシンプルなモデルを考えます。

#  changed         :bool      not null
class SomeModel
    validates :changed, presence: true

    def initialize
        self.changed = false
    end

    def change
        self.changed = true
        # このメソッド内でレコード保存する何らかの処理
    end
end

このモデルのテストを以下のように書いて

「あれ、changedの値を更新したはずなのに、されてなくてテストが落ちるぞ!」

という現象でお悩みではないですか?

subject { some_model.change }
it { expect { subject }.to change(some_model.reload.changed, true) }
# some_model.reload.changedがtrueとなっているはずなのにfalseのまま

もしかするとchangeメソッドが原因かもしれません。
changeメソッドは括弧で括った場合、some_model.reloadが更新処理の実行前にしか評価されないからです。

詳細

まず、このitブロックは以下の順番で処理されます。

  1. changeメソッドのパラメータが評価される
    • つまり、some_model.reloadがこの時点で評価されます
  2. subjectが実行されて更新処理が走る
  3. 1で評価された時点のモデルのインスタンス変数changedとsubject実行後のchangedを比較する

これ、model.reloadが最初に走っちゃってるんですよね。
更新処理実行後にmodel.reloadが走ってないと正しい結果は得られません。

ではどうすればいいのでしょう?

解決策

changeメソッドを以下のようにブロックで記述します。
そうするとsome_model.reloadsubjectの実行前だけでなく実行後にもう一度実行されます。

change {some_model.reload.changed, true}

こうすることで、更新処理実行後にもインスタンスが評価されるので正しい結果が得られます。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?