【问题标题】:Stubbing a helper method from inside helper specs从内部辅助规范中存根辅助方法
【发布时间】:2014-03-24 06:51:34
【问题描述】:

我正在构建一个 Rails 应用程序并使用 RSpec 制定测试。

我为我正在创建的名为current_link_to 的方法编写了测试。该方法应该检查当前页面是否与我传递的路径相对应,并在生成的链接中添加current类。

这是规格:

require "spec_helper"

describe ApplicationHelper do
  describe "#current_link_to" do
    let(:name)     { "Products" }
    let(:path)     { products_path }
    let(:rendered) { current_link_to(name, path) }

    context "when the given path is the current path" do
      before { visit(path) }

      it "should return a link with the current class" do
        # Uses the gem "rspec-html-matchers" (https://github.com/kucaahbe/rspec-html-matchers)
        expect(rendered).to have_tag("a", with: { href: path, class: "current" }) do
          with_text(name)
        end
      end
    end

    context "when the given path is not the current path" do
      before { visit(about_path) }

      it "should return a link without the current class" do
        expect(rendered).to have_tag("a", with: { href: path }, without: { class: "current" } ) do
          with_text(name)
        end
      end
    end
  end
end

然后我尝试按照规范实现我的方法:

module ApplicationHelper
  def current_link_to(name, path, options={})
    options.merge!({ class: "#{options[:class]} current".strip }) if current_page?(path)

    link_to(name, path, options)
  end
end

但是,测试失败并出现以下错误:

Failure/Error: let(:rendered) { current_link_to(name, path) }

RuntimeError: You cannot use helpers that need to determine the current page unless your view context provides a Request object in a #request method

由于我真的不需要 current_page? 辅助方法来对请求进行检查,因此我认为存根它是有意义的。

我尝试了以下方法,但都没有奏效:

  1. helper.double(:current_page? => true)
    似乎存根 helper.current_page? 方法,但它与我的函数调用的方法不同。
  2. allow(ActionView::Helpers::UrlHelper).to receive(:current_page?).and_return(true)
    存根似乎根本没有效果

在写这个问题时我偶然发现了解决方案。我设法在before 块中使用它来存根current_page? 方法:

allow(self).to receive(:current_page?).and_return(true)

它有效,但是这个解决方案提出的问题比它真正回答的要多。我现在对它是如何工作的感到困惑,因为before 块中的self 会回应这似乎很奇怪current_page? 并且该方法实际上与我的助手调用的方法完全相同。

即使在阅读了文档并试图通过在我的代码中乱扔puts 调用来弄清楚它是如何工作的之后,以下疑问仍然困扰着我:

  • 为什么辅助方法可以直接在规范中使用,而 the RSpec docs mention 却应该作为 helper 对象上的方法在所有辅助规范中都可用?
  • 在 RSpec before 块中对 self 的 current_page? 方法存根如何以某种方式反映由我的助手调用的实际方法?我的助手中的self 是否出于某种原因引用了您可以在before 块中找到的相同self? RSpec 或 Rails 是否在幕后包含和混合内容?
  • 如果相同的self 包含我的规范和我的助手,那么在这种情况下self 到底指的是什么?为什么到处都是一样的?

如果有人能帮我解决这个问题,那就太好了,因为这让我大吃一惊,而且我害怕使用我不太了解的代码。

【问题讨论】:

  • 我很好奇,visit() 在这些测试中做了什么?是水豚的吗?
  • 是的,但据我所知,它并没有真正起到任何作用。
  • 我认为这些方法对于辅助规范会失败。我认为当您可以在测试中调用这些方法时有问题。如果你能做到这一点,我认为这也可能是你的问题的根源,因为你在测试中存根本地实例,而不是在服务器中由 capybara 生成的执行测试的实例。
  • 无论您尝试做什么,我认为您的设置存在根本错误
  • 你能分享你的spec_helper吗?

标签: ruby-on-rails ruby rspec rspec-rails


【解决方案1】:

恕我直言,您在这里测试的功能有点过多。诀窍是只测试您需要测试的位。

在这种情况下,您只需要测试当前类是否在需要时添加,而不是在不需要时添加。

这段代码应该可以为您解决问题:

require 'rails_helper'

# Specs in this file have access to a helper object that includes
# the ApplicationHelper. 
RSpec.describe ApplicationHelper, type: :helper do

  describe 'current_link_to' do
    let(:subject) { helper.current_link_to('some_name', 'some_path', options = {}) }

    context 'where the path is current' do
      before do
        allow(helper).to receive(:current_page?).and_return true
      end
      it 'should include the current class' do
        expect(subject).to match /current/
      end
    end

    context 'where the path is not current' do
      before do
        allow(helper).to receive(:current_page?).and_return false
      end
      it 'should not include the current class' do
        expect(subject).to_not match /current/
      end
    end
  end
end

我有点油嘴滑舌,只测试了返回字符串中是否存在“当前”。如果您想更精确,可以测试类似 'class="current"' 的内容。

另一个关键是页面顶部的注释,Rails 会为您插入空白帮助规范:

# Specs in this file have access to a helper object that includes
# the ApplicationHelper. 

这意味着您可以在上面的评论中使用“self”的地方使用“helper”,这使事情变得更清晰(恕我直言)

希望对你有帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    相关资源
    最近更新 更多