开发者

Optionally testing caching in Rails 3 functional tests

Generally, I want my functional tests to not perform action caching. Rails seems to be on my side, defaulting to config.action_controller.perform_caching = false in environment/test.rb. This leads to normal functional tests not testing the caching.

So how do I test caching in Rails 3.

The solutio开发者_如何学Pythonns proposed in this thread seem rather hacky or taylored towards Rails 2: How to enable page caching in a functional test in rails?

I want to do something like:

test "caching of index method" do
  with_caching do
    get :index
    assert_template 'index'
    get :index
    assert_template ''
  end
end

Maybe there is also a better way of testing that the cache was hit?


A solution for rspec:

Add an around block with a custom metadata key to your configuration.

RSpec.configure do |config|
  config.around(:each, :caching) do |example|
    caching = ActionController::Base.perform_caching
    ActionController::Base.perform_caching = example.metadata[:caching]
    example.run
    Rails.cache.clear
    ActionController::Base.perform_caching = caching
  end
end

Add the metadata key when caching is needed.

describe "visit the homepage", :caching => true do
  # test cached stuff
end


You're liable to end up with tests stomping on each other. You should wrap this up with ensure and reset it to old values appropriately. An example:

module ActionController::Testing::Caching
  def with_caching(on = true)
    caching = ActionController::Base.perform_caching
    ActionController::Base.perform_caching = on
    yield
  ensure
    ActionController::Base.perform_caching = caching
  end

  def without_caching(&block)
    with_caching(false, &block)
  end
end

I've also put this into a module so you can just include this in your test class or parent class.


My version that works:

RSpec.configure do |config|
  config.around(:each) do |example|
    caching = ActionController::Base.perform_caching
    ActionController::Base.perform_caching = example.metadata[:caching]
    example.run
    Rails.cache.clear
    ActionController::Base.perform_caching = caching
  end
end

Credit to Rossti, but

  1. Cache needs to be cleared between examples
  2. Cache store can not be set differently on examples, only at init in case anyone wondered


This is not an answer, rather a few limitations to be aware of which don't fit in a comment.

  • All the (amazing) answers relying on ActionController::Base.perform_caching will not work for low-level caching (cf this answer). The only option you have is the module independent config.cache_store setting that you set to :null_store.

  • As @viktor-trón said earlier setting the cache_store is not possible between tests, only at init.

  • The cache is shared between environments for the default cache_store. As a result, (i) the cache should be cleared before testing if you fear stuff from your development session, (ii) running tests clears the cache of your other environments. However, your production environment should use something like the mem_cache_store or whatever else more suited for it, so it should be fine.

From the two first points, it seems that testing for low-level caching is not possible as a per example basis.


Here is my current solution to the problem: In environment/test.rb I set

config.action_controller.perform_caching = true

Also, I am monkey patching Test::Unit::TestCase as follows:

class Test::Unit::TestCase
  def setup_with_disable_caching
    setup_without_disable_caching
    disable_caching
  end
  alias_method_chain :setup, :disable_caching

  def disable_caching
    ActionController::Base.perform_caching = false
  end

  def enable_caching(&blk)
    ActionController::Base.perform_caching = true
    if blk
      yield
      disable_caching
    end
  end
end

This allows me to write the following tests:

test "info about caching (with caching)" do
  enable_caching do
    get :about, :locale => :en
    assert_template 'about'
    get :about, :locale => :en
    assert_template nil
  end
end

test "info about caching (without caching)" do
  get :about, :locale => :en
  assert_template 'about'
  get :about, :locale => :en
  assert_template 'about'
end

It's not perfect, but works for now. I am still interested in better ideas!!


To be able to control activation/deactivation of low-level caching I did the following in RSpec. Note that I have set the cache store to :memory_store in environments/test.rb. For development environment I use the redis store, so there are no conflicts between development and tests.

config.before(:suite) do
  ActionController::Base.perform_caching = false

  # deactivate low-level caching for tests
  Rails.cache.instance_eval do
    alias aliased_fetch fetch

    def fetch(*_args)
      yield
    end
  end
end

config.around(:each, :with_caching) do |example|
  # reactivate low-level caching
  Rails.cache.instance_eval do
    def fetch(*args)
      aliased_fetch(*args) { yield }
    end
  end

  was_caching_activated = ActionController::Base.perform_caching
  ActionController::Base.perform_caching = true
  Rails.cache.clear
  example.run
  Rails.cache.clear
  ActionController::Base.perform_caching = was_caching_activated

  # deactivate low-level caching
  Rails.cache.instance_eval do
    def fetch(*_args)
      yield
    end
  end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜