使用RSpec对Rails进行缓存测试

时间:2011-12-12 23:33:06

标签: ruby-on-rails caching testing rspec

我觉得这是一个没有太多记录的话题,至少我在这里找到了关于最佳实践的麻烦。

我使用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

现在我可以添加:touch =>在我的has_many关联的:belongs_to一侧是真的,这将完成我需要的所有事情,以保持这个片段缓存最新,但对于我的生活,我很难弄清楚如何测试它。

Dropping in:touch => true很简单方便,但它将到期逻辑分散到几个地方。我希望有一个RSpec请求规范,它会遍历并检查此行为,这些内容不会发生太大变化,但可以将所有缓存需求放入一个描述应该发生的内容的特定文件中。 / p>

我尝试过这些方法:

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

规范充实了Capybara的步骤,当然正在进行更新,我认为我是在正确的轨道上。但是测试以奇怪的方式闪烁。我会修改规范以输出员工对象cache_key,有时cache_keys会改变,有时候不会改变,有时规范会通过,有时候不会。

这是一个好方法吗?

我知道SO想要回答的问题,所以要开始:当我的测试环境默认没有缓存时,如何设置和拆除此测试以使用缓存?但是,一般情况下,如果您取得了成功,我真的很想听听您如何在应用中成功测试片段缓存。

修改

我接受了cailinanne的答案,因为它解决了我特别询问的问题,但我已经决定,如果你能摆脱它,我甚至不推荐集成测试缓存。

我没有在关联声明中指定触摸,而是创建了一个特定于我的缓存需求的观察者,它直接触及模型,并且正在单独测试它。

我建议如果单独测试一个多模型观察者,还要包括一个检查观察者observe_models的测试,否则你可能会过多地暴露现实。

引导我的具体答案是:https://stackoverflow.com/a/33869/717365

3 个答案:

答案 0 :(得分:7)

首先我要说的是,在这个答案中,你可能会得到更多的同情。我一直在努力解决这些问题。虽然我能够获得特定测试的可重复结果,但我发现结果根据我是否运行一个与多个规格,以及是否在spork之内而有所不同。叹息。

最后,如果我在test.rb文件中启用了缓存,我发现99.9%的问题都消失了。这可能听起来很奇怪,但经过一些人认为这对我的申请来说是“正确的”。绝大多数测试在视图/请求层都,对于少数测试,在用户查看的相同配置下进行测试是否有意义?

当我正在努力解决这个问题时,我编写了一个blog post,其中包含一些用于测试缓存的有用测试帮助程序。您可能会发现它很有用。

答案 1 :(得分:3)

以下是我在我的配置/环境/ 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

答案 2 :(得分:0)

我使用视图规格来测试视图中的缓存过期时间:

describe Expire::Employee, type: :view, caching: true do

  def hour_ago
    Timecop.travel(1.hour.ago) { yield }
  end

  def text_of(css, _in:)
    Nokogiri::HTML(_in).css(css).text
  end

  let(:employee) { hour_ago { create :employee } }

  def render_employees
    assign(:employees, [employee.reload])
    render(template: 'employees/index.html.erb')
  end
  alias_method :employees_list, :render_employees

  context "when an employee's position gets changed" do
    let(:position) { create :position, employee: employee }
    before { hour_ago { position.update!(name: 'Old name') } }
    let(:update_position) { position.update!(name: 'New name') }

    it "should expire the employee's cache" do
      expect { update_position }
        .to change { text_of('.positions', _in: employees_list) }
        .from(/Old name/).to(/New name/)
    end
  end

  # similar spec case for home base assignment
end

其中

  • Timecop gem用于及时传播,以确保不同雇员的不同缓存版本的缓存密钥时间戳不同
  • Nokogiri宝石用于从渲染的视图中提取员工职位的文本

请注意,我将此规格标记为caching: true。它在每个测试用例之前启用缓存,并在其后禁用:

config.before(:each, caching: true) do
  controller.perform_caching = true
end
config.after(:each, caching: true) do
  controller.perform_caching = false
end

您可能想添加一个示例来检查员工是否已实际上被缓存

describe Expire::Employee, type: :view, caching: true do
  context 'with an uncached employee' do
    it 'should cache the employee' do
      expect_any_instance_of(Employee)
        .to receive(:current_positions).once

      2.times { render_employees }
    end
  end

  # other spec cases
end