You are on page 1of 33

Building a Flexible &

E t
Extensible
ibl F
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
q Requirement
q Changes
g
• Complex Web UI
• Multiple
p Projects
j to Test
• 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
p g of test cases
– 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
– Randomly
R d l generate
t structured
t t dd data
t
Selected Tools

• Selenium RC (http://www.openqa.org/selenium/)
( p p q g )

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

When page first loads


loads… Now ready
ready…

selenium.waitForCondition(" Name: btnG


selenium.isElementPresent('orderBtn')
&& selenium.isEditable('orderBtn')“,
timeout);
Selected Tools

• TestNG ((http://testng.org/doc/index.html)
p g g )

– @Test configurations @DataProvider(name ="customerDataProvider")


public Object[][] customerProvider() {
– Data providers }
...

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

Application
GUI Map Data Map
Configuration

UI Objects SeleniumHelper Util DataGenerator

Base Client Base Test

Application-Specific Application-Specific
Uses
Client 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">
< i l
<uielements>
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>
</category> references and o
overrides
errides

<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> <data name=“searchData">
<uiobject name="search.query" <properties>
type="text"> <prop name="search.query">
<locator>q</locator> United States</prop>
</uiobject> <prop
p p name="search.button"/>/
</properties>
<uiobject name="search.button" </data>
type="button”>
<locator>btnG</locator>
</ i bj t>
</uiobject>
</uielements>
</page>


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
Client s high level
method to implement the
tests.
• Verifies
V ifi th the output
t t matches
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

G
GoogleSearchClient
l S hCli t Uses G
GoogleSearchTest
l S hT t

... ...
public TableObject basicSearch(data); public void basicSearchTest(DataSet dataset){
assertNotEmpty(client.basicSearch(
public TableObject advancedSearch(data); 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
bli class
l BlurDecorator extends
t d UIObj
UIObjectDecorator
tD 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">
<l
<locator>tr[${rownum}]/td[3]/a</locator>
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 <appconfig>
...
<host>${sys.testHost}</host>
• UI Object <user>${env.USER_NAME}<user>
...
• Data </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 After

L
Location:
ti Location:

<uiobject name="location" type="text"> <uiobject name="location" type="select">


<locator>location</locator> <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(
A
Arrays.asList(posibleOptions),count);
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"/>
</d t
</dataset>
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
j Bologna
g
– Ariel Rodriguez
– Nicolas Frontini
– Raul Bajales
– Andrew Salamatov
Q&A

You might also like