SlideShare a Scribd company logo
1 of 69
Download to read offline
Page Objects 
Oren Rubin Testim.io
About Me
Talk Topics 
● Page Objects 
○ Why? 
○ How? 
○ When? 
● Synchronously 
○ Integrate in Page Objects 
○ Remove magic sleep
Talk Topics (we won't discuss) 
● Locators - best practice 
● Retrys 
○ Locator retry (SPA) 
○ Entire Test (stability)
Page Objects? 
A Design Pattern. 
Provides a programmatic API to drive and 
interrogate a UI
Naming things is hard 
● Originally "Window Drivers" - Martin Fowler, 2004 
● Only pages? what about: 
○ Header/footer 
○ Components/widgets 
○ Simple HTML elements (e.g., Tables)
Page Object Pattern 
Expose the service you're interacting with, not the 
implementation. -- Selenium Wiki 
If you have a WebDriver APIs in your test methods... 
You're doing it wrong. -- Simon Stewart
Step 1 - Expose The Service
Step 1 - Expose The Service 
<form id="gaia_loginform"> 
<input id="email"> 
<input id="passwd"> 
<input id="signIn" type="submit"> 
</form>
Step 1 - Expose The Service 
void testMyApp() { 
// login 
driver.findId("email").setText("u@app.com"); 
driver.findId("passwd").setText("12345"); 
driver.findId("signIn").click();; 
sleep(3000); // wait till page loads 
assert(...) // my assertions 
}
Step 1 - Expose The Service 
void testMyApp() { 
// login 
driver.findId("email").setText("u@app.com"); 
driver.findId("passwd").setText("12345"); 
driver.findId("signIn").click();; 
sleep(3000); // wait till page loads 
assert(...) // my assertions 
} 
Simon says no!
Test Automation 
the implementation of the automatic execution of some Business Logic 
Business Logic testMyApp() { 
testMyApp() { 
account.login(); 
gallery.showImage() 
} 
Implementation 
Class LoginPage() { 
login() { 
account.login(); 
// selenium code 
gallery.showImage() 
} 
} 
} 
Class GalleryPage() { 
showImage() {...} 
}
Step 1 - Expose The Service 
loginPage = new LoginPage(); 
loginPage.login(); 
public class LoginPage { 
public login(); 
}
Step 1 - Expose The Service 
loginPage = new LoginPage(); 
loginPage.login(); 
// compilation error: missing return type 
public class LoginPage { 
public ? login(); 
}
Step 1 - Expose The Service 
Option 1: void 
public class LoginPage { 
public void login(); 
}
Step 1 - Expose The Service 
void testMyApp() { 
// TODO move to @setup 
loginPage = new LoginPage(); 
loginPage.login(); 
sleep(3000); // wait till page loads 
assert(…) 
}
Step 1 - Expose The Service 
Pro - Only deals with login page 
● Don't interleave code relevant to other pages in 
this class. 
Con - Only deals with login page 
● Was the login successful? 
● On which page should we be? 
● Is the new page ready?
Step 1 - Expose The Service 
Option 2 (improved): Return a page object 
public class LoginPage { 
public GalleryPage login() {{ 
… 
return new GalleryPage(); 
} 
}
Step 1 - Expose The Service 
void testMyApp() { 
// TODO consider moving to @before 
loginPage = new LoginPage(); 
galleryPage = loginPage.login(); 
sleep(3000); 
galleryPage.showImageFullscreen(); 
assert(…) 
}
Q: What's the source of all evil? 
"No more war - no more blood shed"
A: Random waits 
random sleep 
"No more war - no more blood shed" 
Abie Nathan 
The voice of peace
Step 2 - Eliminate random sleep 
void testMyApp() { 
loginPage = new LoginPage(); 
galleryPage = loginPage.login(); 
sleep(3000); // should we move it? 
galleryPage.showImageFullscreen(); 
assert(…) 
}
Step 2 - option 2 
public class LoginPage { 
public GalleryPage login() { 
… 
sleep(3000); 
return new GalleryPage(); 
} 
}
Step 2 - option 2 
void testMyApp() { 
loginPage = new LoginPage(); 
// synchronous for testers 
galleryPage = loginPage.login(); 
galleryPage.showImageFullscreen(); 
assert(…) 
}
Step 2 - back to option 1 
public class LoginPage { 
void login() {…} 
} 
public class GalleryPage { 
void showImageFullscreen() {…} 
static GalleryPage waitForPage() {…} 
}
Step 2 - back to option 1 
void testMyApp() { 
loginPage = new LoginPage() 
loginPage.login(); // login() is void 
galleryPage = GalleryPage.waitForPage(); 
galleryPage.showImageFullscreen(); 
assert(…) 
}
Step 2 - Combining options 1+2 
public class LoginPage { 
public GalleryPage login() { 
… 
// return new GalleryPage(); 
return GalleryPage.waitForPage(); 
} 
}
Step 2 - Force API comformance 
public class LoginPage { 
static LoginPage waitForPage() {…} 
GalleryPage login() {…} 
} 
public class GalleryPage { 
static GalleryPage waitForPage() {…} 
void showImageFullscreen() {…} 
}
Step 2 - Basic code reuse 
abstract class BasicPage { 
// force derived classes 
public static BasicPage waitForPage(); 
} 
public class GalleryPage { 
public static BasicPage waitForPage() {…} 
}
Step 2 - Basic code reuse 
abstract class BasicPage { 
// force derived classes 
public static BasicPage waitForPage(); 
} 
public class GalleryPage { 
public static BasicPage waitForPage() {…} 
} Computer says no! 
Cannot override static methods!
Step 2 - Basic code reuse 
abstract class BasicPage { 
// force derived classes to implement 
public BasicPage waitForPage(); 
} 
public class GalleryPage { 
public BasicPage waitForPage() {…} 
} Computer says ok! 
But could be improved!
Step 2 - Basic code reuse 
abstract class BasicPage { 
// force derived classes to implement 
public BasicPage waitForPage(); 
} 
public class GalleryPage { 
public GalleryPage waitForPage() {…} 
}
Step 2 - Basic code reuse 
Another option is to use c'tor as wait 
public class GalleryPage { 
GalleryPage() { 
sleep(3000); 
} 
}
Step 2 - Basic code reuse 
Tip! 
Add all common utilities to base class 
abstract class BasicPage { 
public BasicPage waitForPage(); 
public void waitForSpinnerToFade(); 
}
What's next? 
Support Different Users
Step 3 - Params and overload 
Sounds simple! 
public class LoginPage { 
public GalleryPage login(user, password); 
}
Step 3 - Params and overload 
void testMyApp() { 
// bad password 
loginPage = new LoginPage(); 
loginPage = loginPage.login('a', 'wrong'); 
// good password 
galleryPage = loginPage.login('a', 'correct'); 
galleryPage.showImageFullscreen(); 
}
Step 3 - Params and overload 
What about different roles? what about failures 
public class LoginPage { 
public GalleryPage login(user, password); 
public OtherPage login(user, password); 
public LoginPage login(user, password); 
} 
// compilation error: 
can't distinguish overload by return type
Step 3 - Params and overload 
Compiles successfully 
public class LoginPage { 
public GalleryPage loginAsRegular(…); 
public OtherPage loginAsAdmin(…); 
public LoginPage loginAsBadCredintial(…); 
}
Step 3 - Overloading Philosophy 
The LoginPage is used for: 
1. Setup 
Drive the app to a specific state 
No one cares about the implementation 
2. Test the Login page itself 
Test the specific implementation
Step 3 - Overloading Philosophy 
1. Setup 
// look ma! no params! 
loginPage.login(); 
Implementation might be using 
● Username/password 
● Cookies 
● Google Account 
Might be hardcoded, or using config files
Step 3 - Overloading Philosophy 
2. Test the Login page itself 
○ Abstract everything 
login(username, password) 
○ Act on element wrappers (get/set kind) 
Definition: InputDriver getPasswordField() 
Usage: loginPage.getPasswordField.set('12345') 
* less recommended
Step 3 - Overloading Philosophy 
Should we put everything together? 
class LoginPage { 
login() {} 
login(username, password){} 
}
Step 3 - Overloading Philosophy 
Do we want more abstraction 
interface LoginPage { 
login(); 
LoginPageDriver getDriver(); 
} 
interface LoginPageDriver { 
login(username, password); 
}
Step 3 - Overloading Philosophy 
class LoginPageImpl 
implements LoginPage, LoginPageDriver { 
login() {} 
login(username, password); 
LoginPageDriver getDriver() { 
return this; 
} 
}
Step 4 - Page Factory 
public class LoginPage { 
private WebDriver driver; 
public LoginPage(driver) { 
this.driver = driver; 
} 
public GalleryPage login(username, password) { 
driver.findId("email").setText(username); 
… 
}
Step 4 - Page Factory 
public class LoginPage { 
private WebElement email; 
public LoginPage(driver) { 
email = driver.findById("email"); 
} 
public GalleryPage login(username, password) { 
email.setText(username); 
… 
}
Step 4 - Page Factory 
public class LoginPage { 
private WebElement email; 
private WebElement password; 
public LoginPage(driver) { 
email = driver.findById("email"); 
password = driver.findById("password"); 
// Linting error! too much glue code 
} 
}
Step 4 - Page Factory 
public class LoginPage { 
private WebElement email; 
private WebElement password; 
/* look ma.. no ctor! */ 
} 
// loginPage = new LoginPage(); 
loginPage = 
PageFactory.initElement(driver, LoginPage.class)
Step 4 - Page Factory 
Recommended way - in c'tor 
public class LoginPage { 
LoginPage() { 
// wait till ready 
sleep(3000); 
// init 
PageFactory.initElement(driver, this) 
} 
}
Step 4 - Page Factory Pseudo Code 
class PageFactory { 
public static initElements(driver, class){ 
obj = class.new(); 
obj.properties.forEach(function(property) { 
if (property.type === WebElement.class) { 
byId = driver.findById(property.name); 
byName = driver.findByName(property.name); 
obj[property.name] = byId || byName 
} 
});
Step 4 - Page Factory 
But this won't pass any code review 
public class GoogleLoginPage { 
private WebElement q; 
// Linting error! name too short 
and non descriptive 
}
Step 4 - Page Factory 
Annotations to the rescue! 
public class GoogleLoginPage { 
@FindBy(how = How.name, using = "q") 
private WebElement searchBox; 
}
Step 4 - Page Factory 
Shorthand FTW! 
public class GoogleLoginPage { 
@FindBy(name = "q") 
private WebElement searchBox; 
} 
Supports id, tagName and custom annotations!
Step 4 - Assertions 
Two options 
● Separate from Page Objects 
Community Recommends 
● Inside Page Object 
Maybe inside the BasicPage class
Off topic - No more sleep 
public class GalleryPage { 
public void waitForPage() { 
sleep(3000); 
} 
}
Off topic - No more sleep 
Some solutions 
1. The "No smoke without fire" - 
Wait for another element we know that loads last 
2. The "Take the time" 
Wait till state is what you expect (element exists, 
row count,..). Selenium's implicit wait helps. 
3. The "Coordinator" - Recommended! 
Wait for a sign from AUT
The Coordinator 
login() 
waitForTestEvent('logged') 
gallery.showImage() 
// injected code 
setInterval( function({ 
if (works) { 
// we're done 
callback(); 
} 
), 500ms)
The Coordinator 
Option 1 - Javascript 
The API 
driver.executeAsyncScript("some js.. callback()"); 
Translation - browser runs 
function executeAsync(codeToEval, callback) { 
// evaluated code has access to callback 
eval(codeToEval); 
}
The Coordinator 
Option 1 - Javascript 
The API 
driver.executeAsyncScript("some js.. callback()"); 
Translation - browser runs 
function executeAsync(codeToEval, callback) { 
// name 'callback' might change. last param guaranteed though 
eval(codeToEval); 
}
The Coordinator 
driver.executeAsyncScript(" 
some js.. 
// callback() 
var lastIndex = arguments.length; 
var workingCallback = arguments[lastIndex] 
workingCallback(); // now 
setTimeout(workingCallback, 5000); // later 
");
The Coordinator 
The API 
driver.executeAsyncScript("some js.. callback()"); 
Better implementation 
function executeAsync(codeToEval, callback) { 
// evaluated code has access to callback 
var lastArgument = "arguments[arguments.legnth - 1]" 
eval(" ( function(callback){" +codeToEval+" } )(lastArgument)"); 
}
The Coordinator - option 2 
login() 
waitForTestEvent('logged') 
gallery.showImage() 
// load things 
… 
// ready 
sendTestEvent('logged')
The Coordinator - option 2 
Testers wait for a known event 
public class LoginPage { 
public GalleryPage login() { 
… 
waitForTestEvent('gallery-ready') 
return new GalleryPage(); 
} 
}
The Coordinator - option 2 
Dev add html element in test mode 
<body> 
<div id="app"> app goes here </div> 
<div id="test"> test events go here </div> 
</body>
The Coordinator - option 2 
Imlement waitForTestEvent in base class 
abstract class BasicPage { 
void waitForTestEvent(eventName) { 
By selector = By.CSS("#test ." + eventName) 
driver.waitForElement(selector); 
WebElement element = driver.find(selector); 
driver.removeElement(element); 
} 
}
The Coordinator - option 2 
Dev add html element in test mode 
function loadGalleryPage() { 
callServer(function() { 
// page is loaded 
testing.sendTestEvent('gallery-ready') 
}) 
}
The Coordinator - option 2 
Dev add html element in test mode 
class Testing { 
sendTestEvent: function(ev) { 
if (!app.isInTest){ 
return; 
$('#test').append('<div class="+ev+">') 
}) 
} 
Illustration only, don't use jQuery. Use Angular.js / Ember.js
अंत! 
Thank You! 
Oren Rubin 
Testim.io | shexman@gmail | @shexman | linkedin

More Related Content

What's hot

Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and EasybIakiv Kramarenko
 
Webdriver cheatsheets summary
Webdriver cheatsheets summaryWebdriver cheatsheets summary
Webdriver cheatsheets summaryAlan Richardson
 
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Alan Richardson
 
Top100summit 谷歌-scott-improve your automated web application testing
Top100summit  谷歌-scott-improve your automated web application testingTop100summit  谷歌-scott-improve your automated web application testing
Top100summit 谷歌-scott-improve your automated web application testingdrewz lin
 
Basic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersBasic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersDavid Rodenas
 
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...Iakiv Kramarenko
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_twTse-Ching Ho
 
Selenium - The page object pattern
Selenium - The page object patternSelenium - The page object pattern
Selenium - The page object patternMichael Palotas
 
jQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksjQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksAddy Osmani
 
Test automation & Seleniun by oren rubin
Test automation & Seleniun by oren rubinTest automation & Seleniun by oren rubin
Test automation & Seleniun by oren rubinOren Rubin
 
Polyglot automation - QA Fest - 2015
Polyglot automation - QA Fest - 2015Polyglot automation - QA Fest - 2015
Polyglot automation - QA Fest - 2015Iakiv Kramarenko
 
ForwardJS 2017 - Fullstack end-to-end Test Automation with node.js
ForwardJS 2017 -  Fullstack end-to-end Test Automation with node.jsForwardJS 2017 -  Fullstack end-to-end Test Automation with node.js
ForwardJS 2017 - Fullstack end-to-end Test Automation with node.jsMek Srunyu Stittri
 
The Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationThe Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationApplitools
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react formYao Nien Chung
 
Ten Minutes To Tellurium
Ten Minutes To TelluriumTen Minutes To Tellurium
Ten Minutes To TelluriumJohn.Jian.Fang
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentYao Nien Chung
 
Selenium withnet
Selenium withnetSelenium withnet
Selenium withnetVlad Maniak
 

What's hot (20)

Easy tests with Selenide and Easyb
Easy tests with Selenide and EasybEasy tests with Selenide and Easyb
Easy tests with Selenide and Easyb
 
Webdriver cheatsheets summary
Webdriver cheatsheets summaryWebdriver cheatsheets summary
Webdriver cheatsheets summary
 
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
Hands on Exploration of Page Objects and Abstraction Layers with Selenium Web...
 
Top100summit 谷歌-scott-improve your automated web application testing
Top100summit  谷歌-scott-improve your automated web application testingTop100summit  谷歌-scott-improve your automated web application testing
Top100summit 谷歌-scott-improve your automated web application testing
 
Basic Tutorial of React for Programmers
Basic Tutorial of React for ProgrammersBasic Tutorial of React for Programmers
Basic Tutorial of React for Programmers
 
Easy automation.py
Easy automation.pyEasy automation.py
Easy automation.py
 
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
 
20150516 modern web_conf_tw
20150516 modern web_conf_tw20150516 modern web_conf_tw
20150516 modern web_conf_tw
 
Selenium - The page object pattern
Selenium - The page object patternSelenium - The page object pattern
Selenium - The page object pattern
 
jQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & TricksjQuery Proven Performance Tips & Tricks
jQuery Proven Performance Tips & Tricks
 
Intro to ReactJS
Intro to ReactJSIntro to ReactJS
Intro to ReactJS
 
Test automation & Seleniun by oren rubin
Test automation & Seleniun by oren rubinTest automation & Seleniun by oren rubin
Test automation & Seleniun by oren rubin
 
Polyglot automation - QA Fest - 2015
Polyglot automation - QA Fest - 2015Polyglot automation - QA Fest - 2015
Polyglot automation - QA Fest - 2015
 
ForwardJS 2017 - Fullstack end-to-end Test Automation with node.js
ForwardJS 2017 -  Fullstack end-to-end Test Automation with node.jsForwardJS 2017 -  Fullstack end-to-end Test Automation with node.js
ForwardJS 2017 - Fullstack end-to-end Test Automation with node.js
 
The Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better AutomationThe Screenplay Pattern: Better Interactions for Better Automation
The Screenplay Pattern: Better Interactions for Better Automation
 
Lets make a better react form
Lets make a better react formLets make a better react form
Lets make a better react form
 
Ten Minutes To Tellurium
Ten Minutes To TelluriumTen Minutes To Tellurium
Ten Minutes To Tellurium
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 
React js
React jsReact js
React js
 
Selenium withnet
Selenium withnetSelenium withnet
Selenium withnet
 

Similar to Page Objects Pattern for Test Automation

A test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobileA test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobileGlobalLogic Ukraine
 
Desarrollo para Android con Groovy
Desarrollo para Android con GroovyDesarrollo para Android con Groovy
Desarrollo para Android con GroovySoftware Guru
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy codeShriKant Vashishtha
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Python Ireland
 
Android ui layouts ,cntls,webservices examples codes
Android ui layouts ,cntls,webservices examples codesAndroid ui layouts ,cntls,webservices examples codes
Android ui layouts ,cntls,webservices examples codesAravindharamanan S
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to javaciklum_ods
 
Dagger 2 vs koin
Dagger 2 vs koinDagger 2 vs koin
Dagger 2 vs koinJintin Lin
 
Parsing in ios to create an app
Parsing in ios to create an appParsing in ios to create an app
Parsing in ios to create an appHeaderLabs .
 
Unit Test 最後一哩路
Unit Test 最後一哩路Unit Test 最後一哩路
Unit Test 最後一哩路Hokila Jan
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andevMike Nakhimovich
 
WordPress - Custom Page Settings + Salesforce API Integration
WordPress - Custom Page Settings + Salesforce API IntegrationWordPress - Custom Page Settings + Salesforce API Integration
WordPress - Custom Page Settings + Salesforce API IntegrationKhoi Nguyen
 
How to write not breakable unit tests
How to write not breakable unit testsHow to write not breakable unit tests
How to write not breakable unit testsRafal Ksiazek
 
Java ppt Gandhi Ravi (gandhiri@gmail.com)
Java ppt  Gandhi Ravi  (gandhiri@gmail.com)Java ppt  Gandhi Ravi  (gandhiri@gmail.com)
Java ppt Gandhi Ravi (gandhiri@gmail.com)Gandhi Ravi
 
Drupal 8 Every Day: An Intro to Developing With Drupal 8
Drupal 8 Every Day: An Intro to Developing With Drupal 8Drupal 8 Every Day: An Intro to Developing With Drupal 8
Drupal 8 Every Day: An Intro to Developing With Drupal 8Acquia
 
Plug in development
Plug in developmentPlug in development
Plug in developmentLucky Ali
 
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022GoQA
 

Similar to Page Objects Pattern for Test Automation (20)

Javascript Design Patterns
Javascript Design PatternsJavascript Design Patterns
Javascript Design Patterns
 
A test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobileA test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobile
 
Desarrollo para Android con Groovy
Desarrollo para Android con GroovyDesarrollo para Android con Groovy
Desarrollo para Android con Groovy
 
Working effectively with legacy code
Working effectively with legacy codeWorking effectively with legacy code
Working effectively with legacy code
 
Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)Google App Engine in 40 minutes (the absolute essentials)
Google App Engine in 40 minutes (the absolute essentials)
 
Android ui layouts ,cntls,webservices examples codes
Android ui layouts ,cntls,webservices examples codesAndroid ui layouts ,cntls,webservices examples codes
Android ui layouts ,cntls,webservices examples codes
 
Bring the fun back to java
Bring the fun back to javaBring the fun back to java
Bring the fun back to java
 
Dagger 2 vs koin
Dagger 2 vs koinDagger 2 vs koin
Dagger 2 vs koin
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
 
Parsing in ios to create an app
Parsing in ios to create an appParsing in ios to create an app
Parsing in ios to create an app
 
Unit Test 最後一哩路
Unit Test 最後一哩路Unit Test 最後一哩路
Unit Test 最後一哩路
 
Lecture 22
Lecture 22Lecture 22
Lecture 22
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
 
WordPress - Custom Page Settings + Salesforce API Integration
WordPress - Custom Page Settings + Salesforce API IntegrationWordPress - Custom Page Settings + Salesforce API Integration
WordPress - Custom Page Settings + Salesforce API Integration
 
How to write not breakable unit tests
How to write not breakable unit testsHow to write not breakable unit tests
How to write not breakable unit tests
 
Eclipse Tricks
Eclipse TricksEclipse Tricks
Eclipse Tricks
 
Java ppt Gandhi Ravi (gandhiri@gmail.com)
Java ppt  Gandhi Ravi  (gandhiri@gmail.com)Java ppt  Gandhi Ravi  (gandhiri@gmail.com)
Java ppt Gandhi Ravi (gandhiri@gmail.com)
 
Drupal 8 Every Day: An Intro to Developing With Drupal 8
Drupal 8 Every Day: An Intro to Developing With Drupal 8Drupal 8 Every Day: An Intro to Developing With Drupal 8
Drupal 8 Every Day: An Intro to Developing With Drupal 8
 
Plug in development
Plug in developmentPlug in development
Plug in development
 
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022
КОСТЯНТИН КЛЮЄВ «Cypress.io : Let’s go farther» Online QADay 2022
 

Recently uploaded

Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationBradBedford3
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceBrainSell Technologies
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commercemanigoyal112
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)jennyeacort
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样umasea
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 

Recently uploaded (20)

Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
How to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion ApplicationHow to submit a standout Adobe Champion Application
How to submit a standout Adobe Champion Application
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
CRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. SalesforceCRM Contender Series: HubSpot vs. Salesforce
CRM Contender Series: HubSpot vs. Salesforce
 
Cyber security and its impact on E commerce
Cyber security and its impact on E commerceCyber security and its impact on E commerce
Cyber security and its impact on E commerce
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
Call Us🔝>༒+91-9711147426⇛Call In girls karol bagh (Delhi)
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
办理学位证(UQ文凭证书)昆士兰大学毕业证成绩单原版一模一样
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 

Page Objects Pattern for Test Automation

  • 1. Page Objects Oren Rubin Testim.io
  • 3. Talk Topics ● Page Objects ○ Why? ○ How? ○ When? ● Synchronously ○ Integrate in Page Objects ○ Remove magic sleep
  • 4. Talk Topics (we won't discuss) ● Locators - best practice ● Retrys ○ Locator retry (SPA) ○ Entire Test (stability)
  • 5. Page Objects? A Design Pattern. Provides a programmatic API to drive and interrogate a UI
  • 6. Naming things is hard ● Originally "Window Drivers" - Martin Fowler, 2004 ● Only pages? what about: ○ Header/footer ○ Components/widgets ○ Simple HTML elements (e.g., Tables)
  • 7. Page Object Pattern Expose the service you're interacting with, not the implementation. -- Selenium Wiki If you have a WebDriver APIs in your test methods... You're doing it wrong. -- Simon Stewart
  • 8. Step 1 - Expose The Service
  • 9. Step 1 - Expose The Service <form id="gaia_loginform"> <input id="email"> <input id="passwd"> <input id="signIn" type="submit"> </form>
  • 10. Step 1 - Expose The Service void testMyApp() { // login driver.findId("email").setText("u@app.com"); driver.findId("passwd").setText("12345"); driver.findId("signIn").click();; sleep(3000); // wait till page loads assert(...) // my assertions }
  • 11. Step 1 - Expose The Service void testMyApp() { // login driver.findId("email").setText("u@app.com"); driver.findId("passwd").setText("12345"); driver.findId("signIn").click();; sleep(3000); // wait till page loads assert(...) // my assertions } Simon says no!
  • 12. Test Automation the implementation of the automatic execution of some Business Logic Business Logic testMyApp() { testMyApp() { account.login(); gallery.showImage() } Implementation Class LoginPage() { login() { account.login(); // selenium code gallery.showImage() } } } Class GalleryPage() { showImage() {...} }
  • 13. Step 1 - Expose The Service loginPage = new LoginPage(); loginPage.login(); public class LoginPage { public login(); }
  • 14. Step 1 - Expose The Service loginPage = new LoginPage(); loginPage.login(); // compilation error: missing return type public class LoginPage { public ? login(); }
  • 15. Step 1 - Expose The Service Option 1: void public class LoginPage { public void login(); }
  • 16. Step 1 - Expose The Service void testMyApp() { // TODO move to @setup loginPage = new LoginPage(); loginPage.login(); sleep(3000); // wait till page loads assert(…) }
  • 17. Step 1 - Expose The Service Pro - Only deals with login page ● Don't interleave code relevant to other pages in this class. Con - Only deals with login page ● Was the login successful? ● On which page should we be? ● Is the new page ready?
  • 18. Step 1 - Expose The Service Option 2 (improved): Return a page object public class LoginPage { public GalleryPage login() {{ … return new GalleryPage(); } }
  • 19. Step 1 - Expose The Service void testMyApp() { // TODO consider moving to @before loginPage = new LoginPage(); galleryPage = loginPage.login(); sleep(3000); galleryPage.showImageFullscreen(); assert(…) }
  • 20. Q: What's the source of all evil? "No more war - no more blood shed"
  • 21. A: Random waits random sleep "No more war - no more blood shed" Abie Nathan The voice of peace
  • 22. Step 2 - Eliminate random sleep void testMyApp() { loginPage = new LoginPage(); galleryPage = loginPage.login(); sleep(3000); // should we move it? galleryPage.showImageFullscreen(); assert(…) }
  • 23. Step 2 - option 2 public class LoginPage { public GalleryPage login() { … sleep(3000); return new GalleryPage(); } }
  • 24. Step 2 - option 2 void testMyApp() { loginPage = new LoginPage(); // synchronous for testers galleryPage = loginPage.login(); galleryPage.showImageFullscreen(); assert(…) }
  • 25. Step 2 - back to option 1 public class LoginPage { void login() {…} } public class GalleryPage { void showImageFullscreen() {…} static GalleryPage waitForPage() {…} }
  • 26. Step 2 - back to option 1 void testMyApp() { loginPage = new LoginPage() loginPage.login(); // login() is void galleryPage = GalleryPage.waitForPage(); galleryPage.showImageFullscreen(); assert(…) }
  • 27. Step 2 - Combining options 1+2 public class LoginPage { public GalleryPage login() { … // return new GalleryPage(); return GalleryPage.waitForPage(); } }
  • 28. Step 2 - Force API comformance public class LoginPage { static LoginPage waitForPage() {…} GalleryPage login() {…} } public class GalleryPage { static GalleryPage waitForPage() {…} void showImageFullscreen() {…} }
  • 29. Step 2 - Basic code reuse abstract class BasicPage { // force derived classes public static BasicPage waitForPage(); } public class GalleryPage { public static BasicPage waitForPage() {…} }
  • 30. Step 2 - Basic code reuse abstract class BasicPage { // force derived classes public static BasicPage waitForPage(); } public class GalleryPage { public static BasicPage waitForPage() {…} } Computer says no! Cannot override static methods!
  • 31. Step 2 - Basic code reuse abstract class BasicPage { // force derived classes to implement public BasicPage waitForPage(); } public class GalleryPage { public BasicPage waitForPage() {…} } Computer says ok! But could be improved!
  • 32. Step 2 - Basic code reuse abstract class BasicPage { // force derived classes to implement public BasicPage waitForPage(); } public class GalleryPage { public GalleryPage waitForPage() {…} }
  • 33. Step 2 - Basic code reuse Another option is to use c'tor as wait public class GalleryPage { GalleryPage() { sleep(3000); } }
  • 34. Step 2 - Basic code reuse Tip! Add all common utilities to base class abstract class BasicPage { public BasicPage waitForPage(); public void waitForSpinnerToFade(); }
  • 35. What's next? Support Different Users
  • 36. Step 3 - Params and overload Sounds simple! public class LoginPage { public GalleryPage login(user, password); }
  • 37. Step 3 - Params and overload void testMyApp() { // bad password loginPage = new LoginPage(); loginPage = loginPage.login('a', 'wrong'); // good password galleryPage = loginPage.login('a', 'correct'); galleryPage.showImageFullscreen(); }
  • 38. Step 3 - Params and overload What about different roles? what about failures public class LoginPage { public GalleryPage login(user, password); public OtherPage login(user, password); public LoginPage login(user, password); } // compilation error: can't distinguish overload by return type
  • 39. Step 3 - Params and overload Compiles successfully public class LoginPage { public GalleryPage loginAsRegular(…); public OtherPage loginAsAdmin(…); public LoginPage loginAsBadCredintial(…); }
  • 40. Step 3 - Overloading Philosophy The LoginPage is used for: 1. Setup Drive the app to a specific state No one cares about the implementation 2. Test the Login page itself Test the specific implementation
  • 41. Step 3 - Overloading Philosophy 1. Setup // look ma! no params! loginPage.login(); Implementation might be using ● Username/password ● Cookies ● Google Account Might be hardcoded, or using config files
  • 42. Step 3 - Overloading Philosophy 2. Test the Login page itself ○ Abstract everything login(username, password) ○ Act on element wrappers (get/set kind) Definition: InputDriver getPasswordField() Usage: loginPage.getPasswordField.set('12345') * less recommended
  • 43. Step 3 - Overloading Philosophy Should we put everything together? class LoginPage { login() {} login(username, password){} }
  • 44. Step 3 - Overloading Philosophy Do we want more abstraction interface LoginPage { login(); LoginPageDriver getDriver(); } interface LoginPageDriver { login(username, password); }
  • 45. Step 3 - Overloading Philosophy class LoginPageImpl implements LoginPage, LoginPageDriver { login() {} login(username, password); LoginPageDriver getDriver() { return this; } }
  • 46. Step 4 - Page Factory public class LoginPage { private WebDriver driver; public LoginPage(driver) { this.driver = driver; } public GalleryPage login(username, password) { driver.findId("email").setText(username); … }
  • 47. Step 4 - Page Factory public class LoginPage { private WebElement email; public LoginPage(driver) { email = driver.findById("email"); } public GalleryPage login(username, password) { email.setText(username); … }
  • 48. Step 4 - Page Factory public class LoginPage { private WebElement email; private WebElement password; public LoginPage(driver) { email = driver.findById("email"); password = driver.findById("password"); // Linting error! too much glue code } }
  • 49. Step 4 - Page Factory public class LoginPage { private WebElement email; private WebElement password; /* look ma.. no ctor! */ } // loginPage = new LoginPage(); loginPage = PageFactory.initElement(driver, LoginPage.class)
  • 50. Step 4 - Page Factory Recommended way - in c'tor public class LoginPage { LoginPage() { // wait till ready sleep(3000); // init PageFactory.initElement(driver, this) } }
  • 51. Step 4 - Page Factory Pseudo Code class PageFactory { public static initElements(driver, class){ obj = class.new(); obj.properties.forEach(function(property) { if (property.type === WebElement.class) { byId = driver.findById(property.name); byName = driver.findByName(property.name); obj[property.name] = byId || byName } });
  • 52. Step 4 - Page Factory But this won't pass any code review public class GoogleLoginPage { private WebElement q; // Linting error! name too short and non descriptive }
  • 53. Step 4 - Page Factory Annotations to the rescue! public class GoogleLoginPage { @FindBy(how = How.name, using = "q") private WebElement searchBox; }
  • 54. Step 4 - Page Factory Shorthand FTW! public class GoogleLoginPage { @FindBy(name = "q") private WebElement searchBox; } Supports id, tagName and custom annotations!
  • 55. Step 4 - Assertions Two options ● Separate from Page Objects Community Recommends ● Inside Page Object Maybe inside the BasicPage class
  • 56. Off topic - No more sleep public class GalleryPage { public void waitForPage() { sleep(3000); } }
  • 57. Off topic - No more sleep Some solutions 1. The "No smoke without fire" - Wait for another element we know that loads last 2. The "Take the time" Wait till state is what you expect (element exists, row count,..). Selenium's implicit wait helps. 3. The "Coordinator" - Recommended! Wait for a sign from AUT
  • 58. The Coordinator login() waitForTestEvent('logged') gallery.showImage() // injected code setInterval( function({ if (works) { // we're done callback(); } ), 500ms)
  • 59. The Coordinator Option 1 - Javascript The API driver.executeAsyncScript("some js.. callback()"); Translation - browser runs function executeAsync(codeToEval, callback) { // evaluated code has access to callback eval(codeToEval); }
  • 60. The Coordinator Option 1 - Javascript The API driver.executeAsyncScript("some js.. callback()"); Translation - browser runs function executeAsync(codeToEval, callback) { // name 'callback' might change. last param guaranteed though eval(codeToEval); }
  • 61. The Coordinator driver.executeAsyncScript(" some js.. // callback() var lastIndex = arguments.length; var workingCallback = arguments[lastIndex] workingCallback(); // now setTimeout(workingCallback, 5000); // later ");
  • 62. The Coordinator The API driver.executeAsyncScript("some js.. callback()"); Better implementation function executeAsync(codeToEval, callback) { // evaluated code has access to callback var lastArgument = "arguments[arguments.legnth - 1]" eval(" ( function(callback){" +codeToEval+" } )(lastArgument)"); }
  • 63. The Coordinator - option 2 login() waitForTestEvent('logged') gallery.showImage() // load things … // ready sendTestEvent('logged')
  • 64. The Coordinator - option 2 Testers wait for a known event public class LoginPage { public GalleryPage login() { … waitForTestEvent('gallery-ready') return new GalleryPage(); } }
  • 65. The Coordinator - option 2 Dev add html element in test mode <body> <div id="app"> app goes here </div> <div id="test"> test events go here </div> </body>
  • 66. The Coordinator - option 2 Imlement waitForTestEvent in base class abstract class BasicPage { void waitForTestEvent(eventName) { By selector = By.CSS("#test ." + eventName) driver.waitForElement(selector); WebElement element = driver.find(selector); driver.removeElement(element); } }
  • 67. The Coordinator - option 2 Dev add html element in test mode function loadGalleryPage() { callServer(function() { // page is loaded testing.sendTestEvent('gallery-ready') }) }
  • 68. The Coordinator - option 2 Dev add html element in test mode class Testing { sendTestEvent: function(ev) { if (!app.isInTest){ return; $('#test').append('<div class="+ev+">') }) } Illustration only, don't use jQuery. Use Angular.js / Ember.js
  • 69. अंत! Thank You! Oren Rubin Testim.io | shexman@gmail | @shexman | linkedin