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
- Cache needs to be cleared between examples
- 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 independentconfig.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 themem_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
精彩评论