This is part of a series on object oriented integration test design. See the other parts:

Service Objects

There's been a shift in Rails the last couple years away from the fat model, skinny controller paradigm. Instead, there's a lot of momentum behind the Service Object Pattern, which allows for encapsulating complex operations in small, focused plain old ruby objects.

There's a lot of advantages to small objects. Most importantly it is perfectly clear what the class does and they are easy to test as a result.

Test Service Objects

Test Suites develop their own bits of complicated logic over time, and the Service Object Pattern can help us here as well.

Let's say you have a cucumber feature that tries to describe an HTML table with a cucumber one:

Then I should see search results like:
| Username | Email Address  |
| DVG      | dvg@github.com |

The default step definition would look something like this:

Then(/^I should see search results like$/) do |table|
  # table is a Cucumber::AST::Table
end

Comparing a Cucumber Table with an HTML table is not a 1-line thing. It is possible, but it takes several steps.

First of all, Cucumber::Ast::Table implements a #diff! method you can use to compare the html table to the expected table. But you need to convert the HTML Table to a 2D array.

Then(/^I should see search results like$/) do |table|
  actual_table = find("#search_results").all("tr").map do |row|
    row.all("td, th").map do |cell|
      cell.text
    end
  end
  expect { table.diff!(actual_table) }.to be_nil
end

So here we do a double #map operation to convert the rows and cells into a 2D array of strings with the cell contents. This will work, but it's not reusable as it currently stands. A test service would serve us well here

class TableComparison
  attr_accessor :cucumber_table, :selector
  def initialize(cucumber_table, selector)
    @cucumber_table = cucumber_table
    @selector = selector
  end

  def matches?
    cucumber_table.diff!(build_2d_array_from_html_table).nil?
  end

private
  def build_2d_array_from_html_table
    find(selector).all("tr").map do |row|
      row.all("td,th"),map { |cell| cell.text }
    end
  end  
end

Now we have an object that can do the heavy lifting for us

Then(/^I should see search results like$/) do |table|
  expect { TableComparison.new(table, "#search_results").matches? }.to be_true
end

Combine Test Services with Page Objects to keep your step definitions clean

As a final step, we'll move the validation logic to the page object, and then simply ask the page if it's correct

# features/support/pages/search_results_page.rb
class SearchResultsPage < SitePrism::Page
  def has_results_matching?(table)
    TableComparison.new(table, "#search_results").matches?
  end
end

# featrues/step_definitions/search_results_page_steps.rb
Then(/^I should see search results like$/) do |table|
  expect(@app.saerch_results_page).to have_results_matching(table)
end