Jiří Zajpt home

Capybara helper classes

While working on one of our internal projects we've needed to verify that a table has right number of rows, proper columns and so on. Given the project was developed using BDD process with usage of Cucumber for features and Capybara for the interaction with HTML page the initial and naive code for Cucumber step looked like this:

PAGES_TABLE_SELECTOR = '.content table.pages'
PAGES_TABLE_ROW_SELECTOR = "#{PAGES_TABLE_SELECTOR} tbody tr"
PAGES_TABLE_TITLE_SELECTOR = "#{PAGES_TABLE_ROW_SELECTOR} td.title"
PAGES_TABLE_ADDRESS_SELECTOR = "#{PAGES_TABLE_ROW_SELECTOR} td.address"
PAGES_TABLE_PUBLISHED_SELECTOR = "#{PAGES_TABLE_ROW_SELECTOR} td.published input[type=checkbox]"

Then /^I should see site listing with (\d+) pages$/ do |pages_count|
 page.should have_selector PAGES_TABLE_SELECTOR
  page.should have_selector PAGES_TABLE_ROW_SELECTOR, count: pages_count
  page.should have_selector PAGES_TABLE_TITLE_SELECTOR, count: pages_count
  page.should have_selector PAGES_TABLE_ADDRESS_SELECTOR, count: pages_count
  page.should have_selector PAGES_TABLE_TITLE_SELECTOR, count: pages_count
end

I think usage of constants to specify selectors is definitely a plus, but even with that (or because of that) the code just looks horribly and also reads horribly.

While speculating how to make the Cucumber step more readable I've realized that perhaps it would be nice to have an helper object that would encapsulate the HTML table. Using the power of RSpec the code that came out is:

Then /^I should see site listing with (\d+) pages$/ do |pages_count|
  pages_table = PagesTable.new(page)
  pages_table.should exist
  pages_table.should have(pages_count).rows
  pages_table.should have(pages_count).title_cells
  pages_table.should have(pages_count).address_cells
  pages_table.should have(pages_count).published_cells
end

And whoala I think the Cucumber step itself is 100% more readable, more conchise. And hiding of pages table selector and other details in a class is a nice bonus. The PagesTable helper class itself looks like this (I've removed methods not used in shown step):

class PagesTable
  TABLE_SELECTOR = '.content table.pages'
  TABLE_ROW_SELECTOR = "#{TABLE_SELECTOR} tbody tr"
  TABLE_TITLE_SELECTOR = "#{TABLE_ROW_SELECTOR} td.title"
  TABLE_ADDRESS_SELECTOR = "#{TABLE_ROW_SELECTOR} td.address"
  TABLE_PUBLISHED_SELECTOR = "#{TABLE_ROW_SELECTOR} td.published input[type=checkbox]"

  def initialize(page)
    @page = page
  end

  def node
    @page.find TABLE_SELECTOR
  end

  def exist?
    @page.has_selector? TABLE_SELECTOR
  end

  def rows
    @page.all TABLE_ROW_SELECTOR
  end

  def title_cells
    @page.all TABLE_TITLE_SELECTOR
  end

  def address_cells
    @page.all TABLE_ADDRESS_SELECTOR
  end

  def published_cells
    @page.all TABLE_PUBLISHED_SELECTOR
  end
end

I think Rails developers tend to forget the power of so-called PORO's - plain old Ruby classes and OOP itself. I hope that this little example is nice example of using a plain class to make the code more readable and it will inspire you to unleash the power of PORO's.

Fork me on GitHub