5

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

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

例如

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ël Witrant 的回答后编辑的)

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)>'
4

2 回答 2

7

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

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

重复

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

如果比声明和 [text, url] 数组并迭代它们更易读和易读,即使在某种自定义匹配器中也是如此。

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

于 2012-08-12T11:41:48.617 回答
4

要定义自定义匹配器,您可以阅读此功能和一些灵感

写这样的东西:

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,您的第一次尝试是编写它的最佳方式:干净且易于阅读。

于 2012-08-11T22:48:49.750 回答