TDD Action Caching in Rails 3

On my current project, we needed to prove that an action cache was working as expected. Alas, the blogosphere had either out-of-date or unhelpful information. So, after many experiments, we came up with an RSpec test that does what we want. It seems ugly to me, and I hope there’s a better way. The names have been changed to protect the guilty. Any resemblances to actual classes and methods are purely coincidental.

We needed to confirm that a certain action was cached. This action is preview in the brands controller. Using the usual Rails url helpers, we construct some fixture data.

But that is still not enough. caches_action
has an interesting performance enhancement; it doesn’t actually set up the action caching unless caching is enabled at class load time. Since we’re not turning caching on until test time, the caches_action method call in the controller class does nothing. We need to re-add it in our test spec.

it "should action cache #preview" do
Rails.cache.clear
BrandsController.caches_action :preview # must be recapitulated to get around load time weirdfullness
get :preview, :brand_id => brand.to_param
ActionController::Base.cache_store.exist?(preview_cache_path).should be_true
end

This is ugly; it doesn’t test very much (except the underlying caching module, and why bother testing the framework). At least it proves to ourselves that the action is cached and the cache key is what we expect.

First, I create a cached object, in this case, just the string ‘CACHED ACTION’ and then I invoke the action, and then, I hope, the cache will be expired.

It doesn’t really matter what happens in the #update method of the BrandsController as long as it updates a Brand object. A sweeper in Rails is a mix of Observer & controller filters, so all you need to do is “declare” it in the controller

We tried mocking first, but couldn’t get it to work. Sweepers are instantiated instances on each pass, so we’d have to mock any instance of the class. But that doesn’t work either! The classes are hooked into the observer chain at boot time, so mocking the class does no good, but the real sweeper fires any way.

[play sad trombone]

We also had loads of fun doing deep dives into the memory_store class to figure out what was actually getting cached (that’s how we discovered that caches_action wasn’t doing the right thing.)