Skip to content

Instantly share code, notes, and snippets.

@gsmendoza
Last active April 2, 2021 20:48
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save gsmendoza/6050250 to your computer and use it in GitHub Desktop.
Save gsmendoza/6050250 to your computer and use it in GitHub Desktop.

PageObject + SimpleDelegator = Awesome Capybara Helpers

I've always liked using the Page Object pattern to write concise, namespaced, and composeable capybara helpers:

When /^I register as a new user$/ do
  NewUserPage.new(self).tap do |page|
    page.visit!
    page.form.fill
    page.form.submit!
  end
end

class PageObject
  attr_reader :page

  def initialize(page)
    @page = page
  end
end

class NewUserPage < PageObject
  def visit!
    page.visit new_user_path
  end

  def form
    @form ||= UserForm.new(self)
  end
end

class UserForm < PageObject
  attr_reader :parent
  
  def initialize(parent)
    @parent = parent
  end

  def fill
    page.find('[name$="[name]"]').set 'George'
    page.find('[password$="[password]"]').set 'password'
  end
  
  def page
    parent.page
  end

  def submit!
    page.find('input[type=submit]').click
  end
end

However, with this PORO implementation, constantly calling page in the PageObject classes can be annoying. It would be nice to call the Capybara helpers directly without having to mix in Capybara::DSL and all those rspec/capybara helpers into PageObject.

Turns out there is. And it's in the standard Ruby library.

SimpleDelegator "provides the means to delegate all supported method calls to the object passed into the constructor". So if I subclass the NewUserPage class from SimpleDelegator:

class NewUserPage < SimpleDelegator
end

And I instantiate it with the the test suite itself:

When /^I register as a new user$/ do
  new_page = NewUserPage.new(self)
  # ...
end

Then Capybara helpers like visit or find automatically become available to new_page! So now I can write the PageObject classes like this:

class NewUserPage < SimpleDelegator
  def visit!
    visit new_user_path
  end

  def form
    @form ||= UserForm.new(self)
  end
end

class UserForm < SimpleDelegator
  def fill
    find('[name$="[name]"]').set 'George'
    find('[password$="[password]"]').set 'password'
  end

  def submit!
    find('input[type=submit]').click
  end
end
@dnesteryuk
Copy link

There is a gem for Capybara which uses this pattern https://github.com/natritmeyer/site_prism, it has a lot of other cool feature.

@gsmendoza
Copy link
Author

@nestd Thanks for the link! It looks like a very nice gem :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment