技術ネタもこちらで書くことにしました!
みなさんはテストを書いていますか?今回は先日RSpecでテストを書いている時に起こったafter_commitが動かないという問題についての内容となっています。
実行環境
- rails 4.2.10
- rspec-rails 3.5.2
RSpecのafter_commitのテストで問題発生
先日、RSpecでafter_commitのテストしようと以下のようなコードを書いたのですが思った通りの結果になりませんでした。
class Model < ActiveRecord::Base
after_commit :do_somthing
private
def do_something
update_columns(total: 100)
end
end
it 'do something after save' do model.save expect(model.total).to eq(100) end
どうにも原因がわからずGoogleで調べてみると、use_transactional_fixturesがtrueとなっているとcommitが行われず、一つ一つのexampleがtransaction内で実行され、テスト後にはロールバックされるようになるということです。
ちなみにRSpecのuse_transactional_fixturesはそのままRails側の設定に渡されるようになっています。
if ::Rails::VERSION::STRING > '5'
self.use_transactional_tests = RSpec.configuration.use_transactional_fixtures
else
self.use_transactional_fixtures = RSpec.configuration.use_transactional_fixtures
end
self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures
https://github.com/rspec/rspec-rails/blob/v3.5.2/lib/rspec/rails/fixture_support.rb#L25-L30
## 強制的にcommitを実行して問題解決
ではどうやってcommitさせるかというと保存処理をしたあとに強制的にtransaction情報をクリアして、そのあとにrun_callbacksというメソッドを使ってcommitを実行させます。
it 'do something after save' do model.save model.send(:force_clear_transaction_record_state) model.run_callbacks(:commit) expect(model.total).to eq(100) end
これでafter_commitが正常に動作し無事テストが通るようになりました。
https://stackoverflow.com/questions/33940268/after-commit-callback-on-update-doesnt-trigger を参考に最初はmodel.send(:clear_transaction_record_state)を試してみたのですがうまくいきませんでした。
Railsのコードを読んでみるとどうやらclear_transaction_record_stateだとtransaction情報がうまくクリアされておらず、正しくはforce_clear_transaction_record_stateを呼び出す必要がありました。
# Clear the new record state and id of a record.
def clear_transaction_record_state #:nodoc:
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
end
# Force to clear the transaction record state.
def force_clear_transaction_record_state #:nodoc:
@_start_transaction_state.clear
end
https://github.com/rails/rails/blob/v4.2.10/activerecord/lib/active_record/transactions.rb#L379-L388
まとめ
それぞれの設定の意味を理解することはとても大事です。恥ずかしながら今回はそれが出来ていなかったために遭遇した問題でした。
みなさんもデフォルトのままで特に気にしていなかった設定を見返してみるのもいいかもしれません。