User:Wjackson64/sandbox

Automated integration testing support here is provided via watir-webdriver, a toolkit implementing the watir interface over WebDriver's relatively solid cross-browser support. This wiki page offers a brief explanation of our framework and best practice specific to it.

Test Organization
Watir integration tests exist for all of our major websites, and live in the site-trunk/tests/watir directory. The directory structure within /watir maps directly to the organization of our test code, and is as follows.

pages
Watir access methods are encapsulated within page objects, which represent individual pages on a given website. Page files can be thought of as satisfying two purposes. The first is to encapsulate watir element identifiers and minimize the frequency with which these occur in the rest of the code base. The second is to provide wrappers for common interactions that may be required over the aforementioned elements. A page containing an account creation form, for example, would provide both a method for pointing at the required email text field and one for setting it’s value in a safe fashion.

pages/widgets
Widget objects are structurally equivalent to page objects in most ways. Unlike page files however, widget files do not map to a single unique page on a website. They typically represent a form or other piece of web content that is repeated multiple times on various pages. Widgets usually see more reuse than page files, so it is important that you determine what ought to be a widget before you start writing code.

behaviors
Behavior files dictate the interactions that we wish to automate and their expected results, over a given page or widget. Behavior files heavily leverage Rspec, and ought to encapsulate all of the potential interactions we might have with a single site element.

contexts
Context files contain a single class implementing the Singleton pattern. These objects carry data that is required to drive specific tests. A test over the checkrates functionality on Sniqueaway, for example, would have a corresponding context file that stored the name of the city we wished to search in and the number of rooms we were attempting to book. This information is then used throughout the various behavior files that make up the test. Certain context classes may contain two inner classes, one representing user entered data and one representing expected system output, based on that data. This dichotomy should only be created where those two values differ enough that one cannot be logically derived from the other.

tests
The files in the /tests directory stitch together behaviors and contexts to mimic a given critical path flow through our website that we wish to check for correctness. Test files are responsible primarily for initializing context data and calling behaviors in a logical order. Test files will sometimes contain snippets of Rspec interaction code that could not easily be fit anywhere else, but this should be avoided as much as possible.

Best Practice
Here we’ll discuss in more detail the structure of our various test components and whatever standards we’ve determined ought to be followed for each.

Naming
For consistency’s sake, the name of the class implemented by a given page file and the name of the actual file should have the following relationship:

ArbitraryPageClassName -> arbitrary_page_class_name.rb

Wrapped page element access
The benefits of limiting repetition of watir element retrieval calls are obvious. Code is made infinitely easier to maintain if changes to the structure of a page do not require that we hunt down all two hundred instances of “@browser.text_field(:id, ‘email_address’)” or what have you, and alter them.

Following the hierarchy of a page’s DOM is also generally advisable. Elements will on occasion have only html attributes that could identify some other, arbitrary element on the page. Some safety is gained by delimiting lookup scope. This also has the benefit of being less ugly than breaking out xpath selectors.

Examples:

def some_page_form_element @browser.form(:id, “signin_form”) end

def some_page_email_form some_page_form_element.text_field(:class, “email field”) end

Wrapped common functionality
In addition to providing access to page elements, we expect that page/widget objects will provide obvious functionality that tests will require. This typically means that form elements should always be accompanied by setters, and links/buttons by methods that click them.

This is made doubly important by the amount of extra code attached to these seemingly simple operations. All site-specific Utils files implement the same retry_helper function, which allows for an interaction to be repeated n (the default is 3) times, or until a certain condition is met. This is used to deal with the flaky behavior exhibited on occasion by the Firefox and Internet Explorer web-drivers.

Examples:

def set_some_page_email_form(text) Utils.retry_helper do            some_page_email_form.set(text) Utils.wait_until { some_page_email_form.value == text } end end

def click_some_page_button Utils.retry_helper do            some_page_button.click Utils.wait_until { @browser.url.include? “/search_results” } end end