Recently, I’ve stumbled upon an interesting talk by Eduardo Gutierrez called “Ambitious Capybara” which sparked my interest. Eduardo suggests we use data attributes instead of our old friend the CSS selector for targeting DOM elements in order to make our tests less brittle.
The problem that he’s trying to address is one that has been bothering me for a long time and that is the fact that labels, classes, values and ids are very likely to be changed unintentionally by a team member without immediately realising the effects of such changes. As an added bonus, fixing those issues is likely to drive anyone insane. I guess this is the hell we’ve all got used to as being part of the package.
To date, there are a few workarounds to the problem but none of them fix the the problem. So let’s see what they are.
The good old class/id prefix
By far the most popular workaround of them all is to visually indicate the fact that the selector is not to be messed with. We do that by giving each element we want to target a prefixed CSS class or id. An example of such class might be .test-search-form
, where the test prefix is something the whole team agrees that it’s going to be the testing prefix.
You should use CSS classes for styling, period. I know we also use them for javascript but I think that javascript suffers from the same problem as our tests do, it relies to much on those brittle CSS selectors.
The titles approach
Another way of targeting is to add title
attributes to each element and use that for targeting but the problem here is that the titles have a side effect that in some cases might not be desired, the fact that the title string is visible on hover.
So while I encourage you make use of titles for accessibility purposes, I don’t recommend it for testing. It’s just not built for that.
The data attributes approach
This one seems to be much better for targeting since it allows for defining specialised selectors which serve a specific purpose. You can also have a style guide for your data attribute selectors and use that without annoying your design team (or is it the other way around?).
So an example of data attribute selector might look like the following:
<a href="http://google.com" data-test-element="Google It">Google It</a>
Let’s add some sugar
You can define some custom matchers on top of the capybara selectors making your tests read better.
# spec/support/data_attr_selectors.rb
module DataAttrSelectors
Capybara.add_selector(:dom_element) do
xpath { |name| XPath.css(“[data-test-element=‘#{name}’]”)
end
end
# spec/support/data_attr_matchers.rb
module DataAttrMatchers
def dom_element(model_name, options = {})
find(:dom_element, model_name, options)
end
end
You need to uncomment the line in your rails_helper.rb
file that loads the support files and to use the matcher, you either include it in the same rails_helper.rb
file or just in those tests that need it (see the example application on GitHub).
Here is Eduardo’s talk at Railsconf 2015:
Conclusion
I think this solution is an improvement over the CSS classes approach. That together with the custom matchers can make your tests both more readable and less brittle.
I haven’t yet thought about how the same approach could be used with javascript but it would definitely be something worth exploring.
Let me know if you know a better approach than the ones listed. I’m interested in what other people are using to solve this problem.