Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails fragment cache testing with RSpec

I feel like this is a not-so-much documented topic, at least I've had a lot of trouble finding our about the best practices here.

I'm fragment caching in the view using a cache_key:

%tbody
  - @employees.each do |employee|
    - cache employee do
      %tr[employee]
        %td= employee.name
        %td= employee.current_positions
        %td= employee.home_base
        %td= employee.job_classes

Now I can add :touch => true on the :belongs_to side of my has_many associations and this will do everything I need to keep this fragment caching up to date, but for the life of me I'm having a hard time figuring out how to test this.

Dropping in :touch => true is easy and convenient but it spreads the expiry logic around a couple places. I'd love to have an RSpec request spec that walks through and checks the behavior on this, something that isn't liable to change much but can bring all the caching requirements into one specific file that describes what is supposed to be occurring.

I tried along these lines:

require 'spec_helper'
include AuthenticationMacros

describe "Employee index caching" do

  before do
    Rails.cache.clear
    ActionController::Base.perform_caching = true
    login_confirmed_employee
  end

  after do
    ActionController::Base.perform_caching = false
  end

  specify "the employee cache is cleared when position assignments are modified"
  specify "the employee cache is cleared when home base assignments are modified"
end

The specs were fleshed out with the Capybara steps of going through and making the updates of course, and I thought I was quite on the right track. But the tests were flickering in weird ways. I would modify the specs to output the employee objects cache_key, and sometimes the cache_keys would change and sometimes not, sometimes the specs would pass and sometimes not.

Is this even a good approach?

I know SO wants questions that are answerable, so to start: how can I set up and tear down this test to use caching, when my test env does not have caching on by default? In general, however, I'd really like to hear how you might be successfully testing fragment caching in your apps if you have had success with this.

EDIT

I'm accepting cailinanne's answer as it addresses the problem that I specifically ask about, but I have decided however that I don't even recommend integration testing caching if you can get away from it.

Instead of specifying touch in my association declarations, I've created an observer specific to my caching needs that touches models directly, and am testing it in isolation.

I'd recommend if testing a mulit-model observer in isolation to also include a test to check the observers observed_models, otherwise you can stub out too much of reality.

The particular answer that lead me to this is here: https://stackoverflow.com/a/33869/717365

like image 686
Blastula Avatar asked Dec 12 '11 23:12

Blastula


2 Answers

Let me first say that in this answer, you may get more sympathy then fact. I've been struggling with these same issues. While I was able to get reproducible results for a particular test, I found that the results varied according to whether or not I ran one versus multiple specs, and within or without spork. Sigh.

In the end, I found that 99.9% of my issues disappeared if I simply enabled caching in my test.rb file. That might sound odd, but after some thought it was "correct" for my application. The great majority of my tests are not at the view/request layer, and for the few that are, doesn't it make sense to test under the same configurations that the user views?

While I was wrestling with this, I wrote a blog post that contains some useful test helpers for testing caching. You might find it useful.

like image 102
cailinanne Avatar answered Nov 14 '22 08:11

cailinanne


Here is what I've used in my specs with caching enabled in my config/environments/test.rb

require 'spec_helper'
include ActionController::Caching::Fragments

describe 'something/index.html.erb' do
  before(:each) do 
    Rails.cache.clear
    render
  end
  it 'should cache my fragment example' do
    cached_fragment = Rails.cache.read(fragment_cache_key(['x', 'y', 'z']))
    cached_fragment.should have_selector("h1")
  end
end
like image 34
Nicholas Lemay Avatar answered Nov 14 '22 09:11

Nicholas Lemay