Last Updated: February 25, 2016
·
3.676K
· wojtha

How to stub save but run the callbacks in the ActiveRecord

TLDR; you can use ActiveSupport::Callbacks#run_callbacks method to run the callbacks manually.

Recently I moved some logic to the ActiveRecord's before_save callback because the logic was tightly coupled to how the model is stored in database and it should be run before each saving to the database to retains models's integrity.

class Activity < ActiveRecord::Base
  has_one :trackable
  has_one :topic
  before_save :set_topic_from_trackable
  # ...
end

However moving this logic to the callback broke my spec for Activity.track method.

describe Activity do

  let(:topic) { Topic.new }
  let(:trackable) { Comment.new(topic: topic))

  before do
    activity = Activity.new
    allow( activity ).to receive(:save!)
    allow( Activity ).to receive(:new) { activity }
  end

  describe '.track' do
    subject{ Activity.track(comment) }

    it "sets topic from trackable automatically"
      expect( subject ).to have_received(:save!)
      expect( subject.trackable ).to eql trackable
      expect( subject.topic ).to eql trackable.topic
    end

  end

end

How we can fix that? Well there is a run_callbacks method at ActiveRecord, which I've found during solving this issue and it can be used like this: some_model.run_callbacks(:save, :before). So I've just add this to the activity.save stub and the spec is green again!

allow( activity ).to receive(:save!) { activity.run_callbacks(:save) }