Decoupling Data from Presentation

Josh Clayton

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!