I'm new to Ruby on Rails and I'm doing http://ruby.railstutorial.org right now.
From what I understand the language is supposed to follow this DRY standard wery strictly but it's so WET when it comes to test driven development in this tutorial.
For example
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) }
Here we have lots of rows that almost looks the same.
I'we tried this
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
This code works but it's looks ugly and it's more rows.
So I want to know if it's an better way to add an array to have_link method.
I now have a great idea but I don't know how to make it work.
This is my helper (it does not look like it did when i created this question. It is edited after an answer from 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
and this should now be my test
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]) }
So this works but it's not user friendly. When I run the test and I have an link does not exist on the page it tells me that I do not have these links. But I will be able to make the helper tell me which link I'm missing. This is my error code
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)>'
You can write a custom matcher, but I think thats not the idea of tests and DRY.
In the code, the DRY mantra encourages to keep every piece of knowledge of your software in a unique and unambiguous place. That is not the goal of the specs. The goal of the specs is to poof the correctness of a software in a explicit and easy to read way.
Repeat
it { should have_link('Users', href: users_path) }
if far more readable and easy to read than declaring and array of [text, url] and iterate over them, even inside some kind of custom matcher.
In test you should prefer readability over conciseness.
To define custom matchers you can read this feature and some inspiration.
And write something like that:
RSpec::Matchers.define :have_links do |expected|
match do |actual|
expected.all? do |name, options|
have_link(name, options).matches?(actual)
end
end
end
But IMO, your first try is the best way to write it: clean and easy to read.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With