Decoupling Data from Presentation

I’m really happy to see a resurgence in an understanding that writing integration tests that use classes and DOM structure to perform assertions within these tests is typically a bad idea. The DOM structure constantly changes but the data displayed (the stuff we want to assert is present on a page) typically doesn’t.

Why write tests against some markup that a designer may change, sometimes often if he’s iterating over a design? It’ll end up causing everyone headaches and frustration.

How can we mark up our documents without performing assertions against a deep nesting of classes and elements while ensuring our data is displayed properly?

Data attributes.

The browsers I care about support HTML5, so why not start using some of its capabilities to clean up some integration tests?

Imagine this scenario:

Scenario: See the site description on the homepage
  When I am on the homepage
  Then I should see the site description

Simple, straightforward, and each step is a similar level of abstraction.

Now imagine this markup:

<section class="primary-content">
  <header>
    <h1>Welcome to the site!</h1>
    <h2>Insert a witty tagline here</h2>
  </header>
  <p class="description">This product will make your life 100 times
  better.</p>
</section>

I could write a step like this:

Then "I should see the site description" do
  page.should have_selector("section.primary-content p.description",
                            text: "This product will make your life 100 times better.")
end

This step is asserting that there’s a section element with a class of primary-content who has a child paragraph tag with a class of description that contains the correct text. Classes change. Structure changes. This is begging to be rewritten.

<section data-role="primary-content">
  <header>
    <h1>Welcome to the site!</h1>
    <h2>Insert a witty tagline here</h2>
  </header>
  <p data-role="description">This product will make your life 100 times better.</p>
</section>
Then "I should see the site description" do
  page.should have_selector("[data-role='primary-content'] [data-role='description']",
                            text: "This product will make your life 100 times better.")
end

Now, the step is asserting that there’s an element with a role of description within an element with a role of primary-content that contains the correct text.

XHTML introduced a role attribute, but it’s not present currently in HTML5. Luckily, data attributes are even more flexible and we can use any conventions we like. data-role and data-state are two of my favorites.

Interested in how to write awesome Cucumber steps? Head to my Test-Driven Rails workshop on March 26th and March 27th where we’ll cover this and other awesome topics on writing great tests!

Pair with one of our expert developers to level up your skills with Coaching by thoughtbot. Save time learning best practices and techniques for reducing technical debt in Ember, Ruby, Haskell, and Go in 1-on-1 sessions tailored to your goals.