`after_commit` callback on `update` doesn't trigger
Asked Answered
S

3

7

I have defined after_commit callback on update. It doesn't trigger in rspec.

Here is my callback :

after_commit :notify_trip, :if => Proc.new { |trip| trip.can_send_schedule_notification? }, on: :update

Here is my rspec code :

@work_order.update_attributes(:status_id => statuses(:scheduled).id, :tour_id => @tour.id)
@work_order.run_callbacks(:commit)
assert_equal 1, ActionMailer::Base.deliveries.size
expect(ActionMailer::Base.deliveries[0].to).to include(@user.email)
expect(ActionMailer::Base.deliveries[0].subject).to include("Your trip has been scheduled!")

Here callback is not calling and ActionMailer::Base.deliveries.size returning 0

Is there any suggestion on this.

Suntan answered 26/11, 2015 at 13:44 Comment(1)
if there is no commit, it won't trigger.Bunting
H
6

TL;DR

@work_order.update_attributes(:status_id => statuses(:scheduled).id, :tour_id => @tour.id)
@work_order.instance_variable_set(:@_start_transaction_state, {})
@work_order.run_callbacks(:commit)

Explanation

I had a same similar situation with Rails 4.0.1:

@work_order = WorkOrder.create(status_id: 1, order_id: 2)
@work_order.update_attributes(status_id: 4, order_id: 5)
@work_order.run_callbacks(:commit)

when the model implementation look something like this:

class WorkOrder < ActiveRecord::Base
  after_commit :notify_trip, :if => Proc.new { |trip| trip.can_send_schedule_notification? }, on: :update
  after_commit :do_something, on: :create
end

and every time when I call @work_order.run_callbacks(:commit) then it would run the after_commit on create method - do_something. That happens because after the @work_order is created by (@work_order = WorkOrder.create(status_id: 1, order_id: 2)) a instance variable called @_start_transaction_state is initialized, but never cleared. @_start_transaction_state is used in ActiveRecord::Transaction module to track a transaction process as far as I understood from reading the code.

So before calling run_callbacks if we clear up @_start_transaction_state, then we will be able to run the after_commit on: update callbacks

@work_order.instance_variable_set(:@_start_transaction_state, {})
@work_order.run_callbacks(:commit) 

There's one more option which is a little bit cleaner:

@work_order.send(:clear_transaction_record_state)
@work_order.run_callbacks(:commit) 

I know that the solution is hacky and I'm not sure if that might introduce some side effects, especially with nested transactions, but was the only variant that worked for me in Rails 4.0.1.

Hartmann answered 7/2, 2017 at 22:22 Comment(0)
Q
4

According to documentation, you need to use the test_after_commit gem to have after_commit hooks get fired in tests. this will be no longer needed on rails 5.0+.

Another alternative could be putting following code at the end of the body of the it block:

subject.run_callbacks(:commit)
Queasy answered 26/11, 2015 at 14:41 Comment(2)
I already tried it but it is not working with on update. If I didn't mention on update with after_commit then it works perfectly.Suntan
try after { subject.run_callbacks(:commit) } then.Queasy
P
0

For me, I found the answer in this comment: https://mcmap.net/q/1481241/-how-can-i-test-an-after_commit-on-create-callback-with-rspec

They key part is instance_variable_set(:@_trigger_update_callback, true)

Pekan answered 26/4, 2024 at 20:19 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.