Building a Flexible & Extensible F E t ibl Framework k Around Selenium

Apple Chow Santiago Etchebehere

Outline

• Our Challenges g • Our Design Decisions • Framework Architecture • Framework Features • Our Experience • Q&A

Our Challenges

• Frequent Requirement Changes q q g • Complex Web UI • Multiple Projects to Test p j • Limited Resources • Browsers, Platforms

Design Decisions

• Simplicity
– – – – –
Isolating application actions Utilities for filling forms Utilities for interacting with tables AJAX handling utilities Uniform way to access UI elements

• Flexibility
– Grouping of test cases p g – Custom place holders (configuration & data) – Ability to support non-standard UI elements

Design Decisions

• Maintainability/Configurability y g y
– Externalized configuration – Reduce data duplication

• Data driven tests
– Execute same test against multiple datasets – R d l generate structured d t Randomly t t t d data

Selected Tools

• Selenium RC (http://www.openqa.org/selenium/) ( p p q g )
– Locators – WaitForCondition – getEval
When page first loads loads… Now ready ready…

xpath://img[@alt=“Google”] Name: q DOM: document.f.q

selenium.waitForCondition(" selenium.isElementPresent('orderBtn') && selenium.isEditable('orderBtn')“, timeout);

Name: btnG

Selected Tools

• TestNG (http://testng.org/doc/index.html) ( p g g )
– @Test configurations – Data providers
@DataProvider(name ="customerDataProvider") public Object[][] customerProvider() { ... }

• Firebug
@Test(dataProvider = "customerDataProvider") public void test(String id, String name) { id ... }

Framework Architecture

Application Configuration

GUI Map

Data Map

UI Objects

SeleniumHelper

Util

DataGenerator

Base Client

Base Test

Application-Specific Client

Uses

Application-Specific Test Class

Application Config

<appconfig> <parameters> <protocol>http</protocol> < t l>htt </ t l> <host>www.google.com</host> <port></port> <pagesXml>googlesearch/uimap.xml</pagesXml> <projectXml>googlesearch/datamap.xml</projectXml> j tX l l h/d t l / j tX l <browserType>FIREFOX</browserType> <browserVersion>2.0</browserVersion> <timeout>300000</timeout> </parameters> / </appconfig>

UI Object

• UI Element Abstraction • read/write interface
public class TextUIObject implements UIObject{ public String read() { return helper.getText(getLocator()) } public void write(String value) { helper.type(getLocator(), value); } }

UI Map

<page name="searchForm"> <uielements> < i l t > <uiobject name="search.query" type="text"> <locator>q</locator> </uiobject> <uiobject name="search.button“ type="button"> <locator>btnG</locator> </uiobject> </uielements> </page>

Common vs. Test-case Data
<category name="search"> <data name="defaultSearch"> <properties> <prop name="search.query">United States</prop> <prop name="search.button"/> </properties> </data> references and o errides overrides </category>

<testcase id="googleSearch001"> <dataset name="searchSet001"> <dataref name="search001Query" ref="search.defaultSearch"> <prop name="search query">Argentina</prop> name="search.query">Argentina</prop> </dataref> </dataset> </testcase>

UI Object and Data Mapping

<page name="searchForm"> name= searchForm > <uielements> <uiobject name="search.query" type="text"> <locator>q</locator> </uiobject> <uiobject name="search.button" type="button”> <locator>btnG</locator> </uiobject> </ i bj t> </uielements> </page>

<data name=“searchData"> <properties> <prop name="search.query"> United States</prop> <prop name="search.button"/> p p / </properties> </data>

… seleniumHelper.fillForm(“searchForm”, “searchData”); …

Application Client
• Contains methods to abstract
interaction with your application.

• Uses Selenium (SeleniumHelper)
to emulate the user action with the browser, using information from configuration files.

• Simplifies test case writing.

Application Test
• Implements the test cases by
feeding the client methods with test case specific data.

• Uses Client's high level Client s
method to implement the tests.

• V ifi th output matches Verifies the t t t h
the expected result.

Test Case flow

Test Case flow

Test Case flow

Test Case flow

GoogleSearch Example

• Clients (GoogleSearchClient) • Tests: hierarchy (e.g. GoogleSearchTest)
BaseClient BaseTest

GoogleSearchClient G l S hCli t

Uses

GoogleSearchTest G l S hT t

... public TableObject basicSearch(data); public TableObject advancedSearch(data); ...

... public void basicSearchTest(DataSet dataset){ assertNotEmpty(client.basicSearch( dataSet.getDataObject("searchData")); ...

Advanced Features
• AJAX Handling Utilities • Custom UI Objects & Decorators • Table Objects j • Placeholders • Data Generator • Database Support

AJAX Handling (Loaded Condition)
<page name="iGoogle"> <page name="search"> <loaded-condition> selenium.isElementPresent("q") && selenium.isElementPresent("btnG") </loaded-condition> …

100

… seleniumHelper.waitForPageToLoad(“iGoogle.search”); …

AJAX Handling (Reload Trigger)

<page name="searchResults"> <loaded-condition> ... </loaded-condition> <uiobject name="nextLink" type="anchor" reloadTrigger="searchResults"> ocato //a[te t() e t ] / ocato <locator>//a[text()='Next']</locator> ...

Custom UI Object
<uiobject name="elapsedTime" type="SuggestTextBox"> gg <locator>q</locator> ...

public class SuggestTextBox extends AbstractUIObject { ... public void write(SeleniumHelper helper, String value) { for (char c : value.toCharArray()) { helper.type(this.getLocator(), c); helper.wait(1000); } } ...

UI Object Decorators

<uiobject name="creditCardNumber" type="text"> <locator>...</locator> <decorator>BlurDecorator</decorator> ...
public class BlurDecorator extends UIObj tD bli l t d UIObjectDecorator { t ...

public void write(String value) { decoratedObject.write(value); selenium.fireEvent(decoratedObject.getLocator(),"blur"); }

Table Object
<table name="questionsTable" >
<locator>//table[@id='questions']</locator> <uiobject name="unreadQuestions" type="img"> <locator>tr[${rownum}]/td[2]/img</locator> </uiobject> <uiobject name=“subject" type="anchor"> <locator>tr[${rownum}]/td[3]/a</locator> <l t >t [${ }]/td[3]/ </l t > ...

...

TableObject questions = new TableObject("questionsTable");

Map<String, String> firstRow = questions.getRow(1); String subject = questions.readCell(1,3); questions.writeCell(1, subject ); questions writeCell(1 "subject");
...

Placeholders
• Configuration • UI Object • Data
<appconfig> ... <host>${sys.testHost}</host> <user>${env.USER_NAME}<user> ... </appconfig>

... <locator>//div[@id='results']/span[${paginationResultNum}]</locator> ...

<data name=“userProfile"> <properties> <prop name=“username“>user_${timestamp}</prop> < “ “> ${ti t }</ > <prop name="name">${dataGenerator.string(10)}</prop> <prop name="email">${dataGenerator.email}</prop> <prop name="age">${dataGenerator.regex([1..9][0..9])}</prop> </properties> </data> /

Automation Process

1. Define application specific settings (e.g. application URL, browser) 2. Define UI elements map 3. Implement an application client to abstract application actions 4. Define common and test case specific data 5. Implement TestNG test cases that map to test case repository

Our Experience

• UI Changes
Before
Location: L ti Location:

After

<uiobject name="location" type="text"> <locator>location</locator> ...

<uiobject name="location" type="select"> <locator>location</locator> ...

UIObject location = page.getUIObject("location"); helper.write(location, countryName);}

Our Experience

• Custom UI Object

public class RandomMultiSelectionUIObject extends SelectableUIObject { public String write(String count) { // Generate a list of random values from the possible options. String[] posibleOptions = helper.getSelectOptions(locator); List<String> values = dataGenerator.takeFromList( Arrays.asList(posibleOptions),count); A Li t( ibl O ti ) t) ... super.write(selectedOptions.toString()); } ...

Our Experience

• i18n Test Data
<testcase id="test_001"> <dataset name="usData"> <dataref name="criteria" ref="contact.defaultSearch"/> </dataset> </d t t> <dataset name="chineseData"> <dataref name="criteria" ref="contact.defaultSearch"/> <prop name="keyword">廣告服務</prop> </dataref> </dataset> </testcase>

@Test(dataProvider="searchDataProvider") @Testcase(id="test_001") void testSearch(DataSet set) { TableObject resutls = getClient().search(set.getDataObject("criteria"));

Our Team

• Special thanks to:
– Albert Chen – Alejandro Bologna j g – Ariel Rodriguez – Nicolas Frontini – Raul Bajales – Andrew Salamatov

Q&A

Sign up to vote on this title
UsefulNot useful