如何保持rspec测试DRY有很多“have_link”

时间:2012-08-11 19:20:01

标签: ruby-on-rails ruby-on-rails-3 rspec tdd railstutorial.org

我是Ruby on Rails的新手,我现在正在做http://ruby.railstutorial.org

根据我的理解,该语言应严格遵循此DRY标准,但在本教程中涉及到测试驱动开发时,它是如此WET。

例如

it { should have_link('Users', href: users_path) }
it { should have_link('Profile', href: user_path(user)) }
it { should have_link('Settings', href: edit_user_path(user)) }
it { should have_link('Sign out', href: signout_path) }

这里我们有很多几乎看起来相同的行。

我试过这个

it "should have following links from this array" do
    [
        ['Users', href: users_path],
        ['Profile', href: user_path(user)],
        ['Settings', href: edit_user_path(user)],
        ['Sign out', href: signout_path]
    ].each { |a| page.should have_link(a[0], a[1]) }
end

此代码有效,但看起来很丑陋而且行数更多。

所以我想知道是否有更好的方法将数组添加到have_link方法。


我现在有个好主意,但我不知道如何让它发挥作用。

这是我的帮手(当我创建这个问题时看起来不像它。它是在MichaëlWitrant回答之后编辑的)

RSpec::Matchers.define :have_these_links do |*links|
    match do |actual|
        links.each do |link|
            have_link(link.first, link.extract_options!).matches?(actual)
        end
    end
end

这应该是我的测试

it { should have_these_links(['Users', href: users_path],
                        ['Profile', href: user_path(user)],
                        ['Settings', href: edit_user_path(user)],
                        ['Sign out', href: signout_path]) }

所以这可行,但它不是用户友好的。当我运行测试并且页面上没有链接时,它告诉我我没有这些链接。但我可以让帮助者告诉我我错过了哪个链接。 这是我的错误代码

   expected #<Capybara::Session> to have these links ["Users", {:href=>"/users"}], ["Test Link", {:href=>"/Does_not_exist"}], and ["Profile", {:href=>"/users/991"}]
 # ./spec/requests/authentication_pages_spec.rb:42:in `block (4 levels) in <top (required)>'

2 个答案:

答案 0 :(得分:7)

您可以编写自定义匹配器,但我认为这不是测试和DRY的想法。

在代码中,DRY口头禅鼓励将您软件的每一条知识都保存在一个独特而明确的地方。 这不是规格的目标。规范的目标是以明确且易于阅读的方式提供软件的正确性。

重复

it { should have_link('Users', href: users_path) }

如果比[text,url]的声明和数组更具可读性和易读性,并且迭代它们,甚至在某种自定义匹配器中。

在测试中,你应该更喜欢可读性而不是简洁。

答案 1 :(得分:4)

要定义自定义匹配器,您可以阅读this feature和一些inspiration

写下这样的东西:

RSpec::Matchers.define :have_links do |expected|
  match do |actual|
    expected.all? do |name, options|
      have_link(name, options).matches?(actual)
    end
  end
end

但IMO,你的第一次尝试是写它的最佳方式:干净易读。