Writing good BDD steps

The tools provided in Aloe-Webdriver form a reasonably thin wrapper around Selenium and thus make it very easy to write imperative tests. While the occasional imperative test is useful, it is frequently more useful to abstract these into sub-steps of a more declarative test.

For example, take this example from the BBC essay: Tips for writing better feature files.

Here is a bad, imperative example:

Given I am on the login page
When I fill in "username" with "ABC"
And I fill in "password" with "XYZ"
And I checked the "Remember Me" checkbox
And I click on the "Submit" button
Then I should log into the system
And I should see "Welcome"

Instead a better, declarative example would be:

Given I have logged into the system
Then I should see "Welcome"

Use step.behave_as() to call the imperative steps from your own step abstracts the mechanics of your website into something more descriptive. This also makes it easier if you ever change the login process.

@step("I have logged into the system")
def i_log_in():
    '''Log in to the site'''
    step.behave_as('Given I am on the login page')
    step.behave_as('When I fill in "username" with "ABC"')
    step.behave_as('And I fill in "password" with "XYZ"')
    step.behave_as('And I checked the "Remember Me" checkbox')
    step.behave_as('And I click on the "Submit" button')
    step.behave_as('Then I should log into the system')

Step Writing Utilities

Aloe-Webdriver includes several utilities for writing Selenium tests.

class aloe_webdriver.util.ElementSelector(browser, xpath=None, elements=None, filter_displayed=False, filter_enabled=False)

A set of elements on a page matching an XPath query.

Parameters:
  • browserworld.browser
  • xpath (str) – XPath query
  • elements (list) – list of selenium.WebElement objects
  • filter_displayed (bool) – whether to only return displayed elements
  • filter_enabled (bool) – whether to only return enabled elements

Delays evaluation to batch the queries together, allowing operations on selectors (e.g. union) to be performed first, and then issuing as few requests to the browser as possible.

One of xpath or elements must be passed. Passing xpath creates a selector delaying evaluation until it’s needed, passing elements stores the elements immediately.

Can behave as an iterable of elements or a single element by proxying all method calls, asserting that there is only one element selected.

Can be combined using the addition operator (+) to OR XPath queries together.

evaluated

Whether the selector has already been evaluated.

filter(displayed=False, enabled=False)

Filter elements by visibility and enabled status.

Parameters:
  • displayed – whether to filter out invisible elements
  • enabled – whether to filter out disabled elements

Returns: an ElementSelector

aloe_webdriver.util.element_id_by_label(browser, label)

The ID of an element referenced by a label`s ``for` attribute. The label must be visible.

Parameters:
  • browserworld.browser
  • label – label text to return the referenced element for

Returns: for attribute value

aloe_webdriver.util.find_any_field(browser, field_types, field_name)

Find a field of any of the specified types.

Parameters:
  • browserworld.browser
  • field_types (list) – a list of field type (i.e. button)
  • value (string) – an id, name or label

Returns: an ElementSelector

See also: find_field().

aloe_webdriver.util.find_button(browser, value)

Find a button with the given value.

Searches for the following different kinds of buttons:

<input type=”submit”> <input type=”reset”> <input type=”button”> <input type=”image”> <button> <{a,p,div,span,…} role=”button”>

Returns: an ElementSelector

aloe_webdriver.util.find_field(browser, field_type, value)

Locate an input field.

Parameters:
  • browserworld.browser
  • field_type (string) – a field type (i.e. button)
  • value (string) – an id, name or label

This first looks for value as the id of the element, else the name of the element, else as a label for the element.

Returns: an ElementSelector

aloe_webdriver.util.find_field_by_id(browser, field_type, id)

Locate the control input with the given id.

Parameters:
  • browserworld.browser
  • field_type (string) – a field type (i.e. button)
  • id (string) – id attribute

Returns: an ElementSelector

aloe_webdriver.util.find_field_by_label(browser, field_type, label)

Locate the control input that has a label pointing to it.

Parameters:
  • browserworld.browser
  • field_type (string) – a field type (i.e. button)
  • label (string) – label text

This will first locate the label element that has a label of the given name. It then pulls the id out of the ‘for’ attribute, and uses it to locate the element by its id.

Returns: an ElementSelector

aloe_webdriver.util.find_field_by_name(browser, field_type, name)

Locate the control input with the given name.

Parameters:
  • browserworld.browser
  • field_type (string) – a field type (i.e. button)
  • name (string) – name attribute

Returns: an ElementSelector

aloe_webdriver.util.find_field_by_value(browser, field_type, name)

Locate the control input with the given value. Useful for buttons.

Parameters:
  • browserworld.browser
  • field_type (string) – a field type (i.e. button)
  • name (string) – value attribute

Returns: an ElementSelector

aloe_webdriver.util.string_literal(content)

Choose a string literal that can wrap our string.

If your string contains a ' the result will be wrapped in ". If your string contains a " the result will be wrapped in '.

Cannot currently handle strings which contain both " and '.

aloe_webdriver.util.wait_for(func)

A decorator to invoke a function, retrying on assertion errors for a specified time interval.

Adds a kwarg timeout to func which is a number of seconds to try for (default 15).