Testing Rails' page caching in RSpec

In preparation for a Ruby project I wanted to play around with Rails’ page caching a little over the weekend. Naturally, I wanted to find a way to ensure that my controller was writing files correctly so that Apache (or any other HTTP server) can then serve the static content directly (without hitting my Rails processes). Since I’m also digging RSpec at the moment, I wanted to find a way of expressing it nicely in my specifications. This is what I ended up with.

Here’s the relevant controller’s specification:

describe UsersController, "caching" do  include CachingExampleHelper  it "should cache new users page" do    requesting {get :new}.should be_cached  end
  it "should not cache the activation page" do    requesting {get :activate, :activation_code => 'blah'}.should_not be_cached  endend

The key part is the requesting line. That’s my extension that encapsulates the request that should be cached, so that we can both make the request and check that it ended up with a file being written to the filesystem.

module CachingExampleHelper  ActionController::Base.public_class_method :page_cache_path  ActionController::Base.perform_caching = true  def requesting(&request)    url = request.call.request.path    ActionController::Base.expire_page(url)
    request.call  end
  module ResponseHelper    def cached?      File.exists? ActionController::Base.page_cache_path(request.path)    end  end
  ActionController::TestResponse.send(:include, ResponseHelper)end

Note that we have to expose the pagecachepath method so we can calculate the location for where the cached files will get written, and then also explicitly enable caching. When we call requesting giving a block to the request we want to make (such as get :index to invoke the index action), we capture the block as a Proc, invoke it and then read the request’s path from the generated TestResponse object. This lets us then first expire the page (in case it was cached previously), and then make the request. Finally, we then check whether the cached file exists by mixing in a cached? method to the Response.

Unfortunately, the code above will make 2 requests per assertion. But, I’d rather have it read nicely for the time being. Any suggestions on how you could tidy this up further would be well received!

I’m not sure I’d want to run these tests all the time, maybe something a little lighter to ensure that we tell Rails to caches_page for relevant actions in our controllers, but as a definite re-assurance it seems ok.