Como fazer stub save, mas executar os callbacks no ActiveRecord

TLDR; você pode usar o método ActiveSupport :: Callbacks # run_callbacks para executar os callbacks manualmente.

Recentemente, movi alguma lógica para o before_saveretorno de chamada do ActiveRecord porque a lógica estava fortemente acoplada a como o modelo é armazenado no banco de dados e deve ser executado antes de cada salvamento no banco de dados para manter a integridade dos modelos.

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

No entanto, mover essa lógica para o retorno de chamada quebrou minha especificação de Activity.trackmétodo.

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

Como podemos consertar isso? Bem, há uma run_callbacks método em ActiveRecordque eu encontrei durante a resolver esta questão e ele pode ser usado como este: some_model.run_callbacks(:save, :before). Acabei de adicionar isso ao activity.saveesboço e a especificação fica verde novamente!

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