Appearance
Composition and Injection 🧩
As described in Using Test Helpers, it's possible to easily combine test helpers.
In this section, we dive into detail on different use cases and ways to solve them.
Understanding Injection
Let's say that we want to leverage some logic from our TableTestHelper
.
class TableTestHelper < BaseTestHelper
def row_for(*cells)
find(:table_row, cells)
end
def have_row(*cells)
have(:table_row, cells)
end
end
We will inject this helper with use_test_helpers
.
class UsersTestHelper < BaseTestHelper
use_test_helpers(:table)
aliases(
el: '.users'
)
def find_user(name)
within { table.row_for(name) }
end
def click_to_edit
click_link('Edit')
end
def have_user(name)
within { table.have_row(name) }
self
end
end
Every time we call table
, an instance of TableTestHelper
is returned.
TIP
Injected test helpers will preserve the assertion state of the current helper.
We can use any assertion without having to explicitly call should
in the table
helper.
users.should.have_user('Jim').should_not.have_user('John')
Wrapping 🎁
Any injected helper can optionally take an element as a parameter, which will be wrapped and used as the initial context for the returned helper.
def find_user(name)
table(self).row_for(name) # same as: table.wrap_element(el).row_for(name)
end
Just as before using within
, it will only find rows inside the .user
element.
users.find_user('Jim')
# same as
find('.users').find(:table_row, ['Jim'])
Wrapping to Chain
In the examples above, find_user
returns an instance of TableTestHelper
:
users.find_user('Jim').click_to_edit
# NoMethodError: undefined method `click_to_edit' for #<TableTestHelper tag="tr">
We can workaround this by wrapping the result:
def find_user(name)
wrap_element table(self).row_for(name)
end
users.find_user('Jim').click_to_edit
# #<UsersTestHelper tag="a">
Inheritance
Another way to share functionality between test helpers is to use inheritance.
Just like methods, any aliases defined in ancestors will also be available in the subclass.
Capybara.get_test_helper_class(:table) # or require_relative './table_test_helper'
class UsersTestHelper < TableTestHelper
aliases(
el: '.users'
)
def find_user(name)
el.row_for(name)
end
def click_to_edit
click_link('Edit')
end
end
Because we are using inheritance, we can skip all the wrapping and context switching from the previous examples.
users.find_user('Jim').click_to_edit
# #<UsersTestHelper tag="a">
Simple is better though, and composition should be preferred over inheritance in most cases.
Using the Current Element
You can leverage the current element as needed when creating your own actions or assertions by calling to_capybara_node
:
def focus
to_capybara_node.execute_script('this.focus()')
self
end
Have in mind that in most cases this is unnecessary, as many actions such as click
already use the current element.