【问题标题】:Rails fragment cache testing with RSpec使用 RSpec 进行 Rails 片段缓存测试
【发布时间】:2012-01-18 22:15:03
【问题描述】:

我觉得这是一个没有太多文档记录的话题,至少我在找到我们的最佳实践方面遇到了很多麻烦。

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

现在我可以在我的 has_many 关联的 :belongs_to 一侧添加 :touch => true ,这将做我需要的一切来保持这个片段缓存是最新的,但是对于我的生活来说,我很难过弄清楚如何测试它。

加入 :touch => true 既简单又方便,但它将到期逻辑分散在几个地方。我希望有一个 RSpec 请求规范,它可以遍历并检查其行为,这不会发生太大变化,但可以将所有缓存要求放入一个描述应该发生的事情的特定文件中。

我尝试了这些方法:

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 的回答,因为它解决了我特别询问的问题,但我决定,如果您可以摆脱它,我什至不推荐集成测试缓存。

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

如果单独测试一个多模型观察者,我建议还包括一个检查观察者观察者模型的测试,否则你可能会忽略太多现实。

让我想到这个的具体答案在这里:https://stackoverflow.com/a/33869/717365

【问题讨论】:

  • 嗨!你最终想出了一个成功的方法吗?为了完整起见,你能把它贴在这里吗?
  • 呵呵,我应该一直读到最后……现在我意识到你决定不那样测试缓存了……

标签: ruby-on-rails caching testing rspec


【解决方案1】:

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

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

当我为此苦苦挣扎时,我写了一个blog post,其中包含一些有用的测试助手来测试缓存。您可能会发现它很有用。

【讨论】:

  • 哈哈,谢谢!事实上,昨天阅读了您的博客文章,但我无法根据自己的情况调整该技术。我将在此线程中发布我的完整性方法。
【解决方案2】:

这是我在规范中使用的,在我的 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

【讨论】:

    【解决方案3】:

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

    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 gem 用于从渲染视图中提取员工职位的文本

    请注意,我使用 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
    

    【讨论】:

      猜你喜欢
      • 2012-03-09
      • 1970-01-01
      • 1970-01-01
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 1970-01-01
      • 2021-07-03
      相关资源
      最近更新 更多