How to test model's callback method independently?

Callback and Callback behavior are independent tests. If you want to check an after_save callback, you need to think of it as two things:

  1. Is the callback being fired for the right events?
  2. Is the called function doing the right thing?

Assume you have the Article class with many callbacks, this is how you would test:

class Article < ActiveRecord::Base
  after_save    :do_something
  after_destroy :do_something_else
  ...
end

it "triggers do_something on save" do
  expect(@article).to receive(:do_something)
  @article.save
end

it "triggers do_something_else on destroy" do
  expect(@article).to receive(:do_something_else)
  @article.destroy
end

it "#do_something should work as expected" do
  # Actual tests for do_something method
end

This decouples your callbacks from behavior. For example, you could trigger the same callback method article.do_something when some other related object is updated, say like user.before_save { user.article.do_something }. This will accomodate all those.

So, keep testing your methods as usual. Worry about the callbacks separately.

Edit: typos and potential misconceptions Edit: change "do something" to "trigger something"


You can use shoulda-callback-matchers to test existence of your callbacks without calling them.

describe Article do
  it { is_expected.to callback(:do_something).after(:save) }
end

If you also want to test the behaviour of the callback:

describe Article do
  ...

  describe "#do_something" do
    it "gives the article something" do
      @article.save
      expect(@article).to have_something
    end
  end
end

In the spirit of Sandi Metz and minimalist testing, the suggestion in https://stackoverflow.com/a/16678194/2001785 to confirm the call to a possibly private method does not seem right to me.

Testing a publicly-observable side-effect or confirming an outgoing command message makes more sense to me. Christian Rolle provided an example at http://www.chrisrolle.com/en/blog/activerecord-callback-tests-with-rspec.