Testing a sweeper with RSpec in Rails

2.7k views Asked by At

I want to make sure my sweeper is being called as appropriate so I tried adding something like this:

it "should clear the cache" do
    @foo = Foo.new(@create_params)
    Foo.should_receive(:new).with(@create_params).and_return(@foo)
    FooSweeper.should_receive(:after_save).with(@foo)
    post :create, @create_params
end

But I just get:

<FooSweeper (class)> expected :after_save with (...) once, but received it 0 times

I've tried turning on caching in the test config but that didn't make any difference.

3

There are 3 answers

0
Patrick Ritchie On BEST ANSWER

As you already mentioned caching has to be enabled in the environment for this to work. If it's disabled then my example below will fail. It's probably a good idea to temporarily enable this at runtime for your caching specs.

'after_save' is an instance method. You setup an expectation for a class method, which is why it's failing.

The following is the best way I've found to set this expectation:

it "should clear the cache" do
  @foo = Foo.new(@create_params)
  Foo.should_receive(:new).with(@create_params).and_return(@foo)

  foo_sweeper = mock('FooSweeper')
  foo_sweeper.stub!(:update)
  foo_sweeper.should_receive(:update).with(:after_save, @foo)

  Foo.instance_variable_set(:@observer_peers, [foo_sweeper])      

  post :create, @create_params
end

The problem is that Foo's observers (sweepers are a subclass of observers) are set when Rails boots up, so we have to insert our sweeper mock directly into the model with 'instance_variable_set'.

0
Mandrake Button On

Sweepers are Singletons and are instantiated at the beginning of the rspec test. As such you can get to it via MySweeperClass.instance(). This worked for me (Rails 3.2):

require 'spec_helper'
describe WidgetSweeper do
  it 'should work on create' do
    user1 = FactoryGirl.create(:user)

    sweeper = WidgetSweeper.instance
    sweeper.should_receive :after_save
    user1.widgets.create thingie: Faker::Lorem.words.join("")
  end
end
0
Mike On

Assuming you have:

  • a FooSweeper class
  • a Foo class with a bar attribute

foo_sweeper_spec.rb:

require 'spec_helper'
describe FooSweeper do
  describe "expiring the foo cache" do
    let(:foo) { FactoryGirl.create(:foo) }
    let(:sweeper) { FooSweeper.instance }
    it "is expired when a foo is updated" do
      sweeper.should_receive(:after_update)
      foo.update_attribute(:bar, "Test")
    end
  end
end