three simple chords of alternative pageobjects and hardcore of loadablecomponents
DESCRIPTION
***VIDEO***: view in SD at http://youtu.be/HPHKeBakulQ or download in HD at http://bit.ly/1nyvA67 Often we have lack of automation resources. If we just would involve less experienced juniors to implement test model and even Manual QA to write DSL like tests… In this talk I want to present the simplified approach to write PageObjects for your test model as it would be like playing “three chords” song on a guitar. And also share the experience of pacifying the LoadableComponent pattern, rather hard in implementation but making your tests much more DRY and easy to use in context of loading pages.TRANSCRIPT
![Page 1: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/1.jpg)
Three simple chords of Alternative “PageObjects” and Hardcore of
LoadableComponents
Iakiv Kramarenko
![Page 2: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/2.jpg)
Conventions :)
● Sympathy colors***:
Green =
Orange =
Red =
*** often are subjective and applied to specific context ;)
![Page 3: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/3.jpg)
● At project with no Web UI automation, no unit testing
● With 4(5) Manual QA
– Using checklists + long detailed End to End test cases
● With 1 QA Automation found
![Page 4: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/4.jpg)
Testing single page web app● Ajax
● only <div>, <a>, <input>
– Inconsistent implementation
● No confident urls
● One current Frame/Page with content per user step
– Deep structure: ● Authorization > Menus > SubMenus > Tabs > Extra > Extra
– Modal dialogs
![Page 5: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/5.jpg)
Met Requirements● Automate high level scenarios
– use AC from stories
– existed Manual Scenarios
● Use 3rd party solutions
– Java Desired
● Involve Manual QA
– provide easy to use solution
– BDD Desired
● In Tough Deadlines :)
![Page 6: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/6.jpg)
Dreaming of framework...● Fast in development
– Using instruments quite agile to adapt to project specifics
● Extremely easy to use and learn
● With DRY and handy
page loading
● Simple DSL for tests
● BDD – light and DRY as much as possible
![Page 7: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/7.jpg)
Choosing Programming ParadigmFor WebUI Automation
Based on https://bitbucket.org/yashaka/oopbucket/src
![Page 8: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/8.jpg)
OOP or not to OOPThat is the question
![Page 9: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/9.jpg)
OOP can impose you to● Learn much
– Concept itself
– Design Patterns
● Have bulky structured implementation
– Coupled via inheritance
– Having too many layers of abstractions
● Work harder to implement DSL
Do you need this?
![Page 10: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/10.jpg)
OOP can give you● Batch common operations on pages/steps ***
E.g.– Reporting per steps
– abstract open() per page
– abstract getExpectedElements() per page
● Obligations over conventions
● Certainty in future refactoring
Do you need this?
![Page 11: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/11.jpg)
Sometimes...● Batch/common operations may be redundant for pages/steps
– Sufficient reporting can be implemented in low-level libraries
– “batch” open() may be called on LoadableComponent separately
– IHaveExpectedElements may give no advantages for smoke testing in your project
● And still can be implemented separately in e.g. LoadableComponent
● Or via Reflection
– Some “common” implementation can be moved from pages to “widgets” and still be implemented with OOP
![Page 12: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/12.jpg)
Sometimes...● Conventions can be very easy
● No severe refactoring is coming
– Test Automation Project is not a NASA Space Shuttle ;).
![Page 13: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/13.jpg)
So Think Always!
![Page 14: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/14.jpg)
Procedural
Functional
OOP
And Balance!
![Page 15: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/15.jpg)
Classic PageObject Pattern
![Page 16: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/16.jpg)
public class LoginClassicPageObject extends BasePage {
@FindBy(css = "#login-form") private WebElement container;
@FindBy(name = "username") private WebElement usernameField;
@FindBy(name = "password") private WebElement passwordField;
@FindBy(css = ".ui-button[value='Log in']") private WebElement loginButton;
public void WebElement getContainer(){ return container; } @Override public void open(String baseurl) { driver.get(baseurl); }
public void doLogin(String login, String pass){
usernameField.sendKeys(login);
passwordField.sendKeys(pass);
loginButton.click();
}
public LoginClassicPageObject(
WebDriver driver, String baseurl) {
PageFactory.initElements(driver, this);
this.driver = driver;
this.baseurl = baseurl;
}
private String baseurl;
private WebDriver driver;
}
![Page 17: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/17.jpg)
Involving
![Page 18: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/18.jpg)
public class LoginSelenidePageObject extends BasePage {
private final String container = "#login-form";
private final By username = By.name("username");
private final By password = By.name("password");
private final String loginButton = ".ui-button[value='Log in']";
public void SelenideElement getContainer(){
return $(container);
}
@Override
public void open(String baseurl) {
open(baseurl);
}
public void doLogin(String login, String password){
$(username).setValue(login);
$(password).sendKeys(password);
$(loginButton).click();
}
public LoginSelenidePageObject(String baseurl) {
this.baseurl = baseurl;
}
private String baseurl;
}
![Page 19: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/19.jpg)
public class LoginSelenidePageObject2 extends BasePage {
public void SelenideElement сontainer(){ return $("#login-form");}
public void SelenideElement usernameField(){ return $(By.name("username"));}
public void SelenideElement passwordField(){ return $(By.name("password"));}
public void SelenideElement loginButton(){ return $(".ui-button[value='Log in']");}
@Override
public void open(String baseurl) {
open(baseurl);
}
public void doLogin(String login, String password){
usernameField().setValue(login);
passwordField().setValue(password);
loginButton.click();
}
public LoginSelenidePageObject(String baseurl) {
this.baseurl = baseurl;
}
private String baseurl;
}
![Page 20: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/20.jpg)
“Procedural” approach to implement “PageObjects”
![Page 21: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/21.jpg)
PageUtilspublic class Login{
public static void open(String baseurl) { Selenide.open(baseurl); }
public static SelenideElement container() { return $("#login-form");}
public static SelenideElement usernameField(){ return $(By.name("username"));} public static SelenideElement passwordField(){ return $(By.name("password"));} public static SelenideElement loginButton(){ return $(".ui-button[value='Log in']");}
public static void doLogin(String login, String password){ usernameField().setValue(login); passwordField().setValue(password); loginButton().click(); }}
AlternatIvE
![Page 22: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/22.jpg)
Or...public class Login{
public static void open(String baseurl) { Selenide.open(baseurl); }
public static final String container = "#login-form"; public static final By username = By.name("username"); public static final By password = By.name("password"); public static final String loginButton = ".ui-button[value='Log in']";
public static void doLogin(String login, String password){ $(username).setValue(login); $(password).sendKeys(password); $(loginButton).click(); }}
AlternatIvE
![Page 23: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/23.jpg)
PageUtils usageLogin.open(baseurl);
Login.doLogin(username, password);
Home.addProduct("Product_1");UserPanel.doLogout();
Login.container().shouldBe(visible);
PageObjects usageloginPage = new LoginPage(baseurl);loginPage.open();loginPage.doLogin(username, password);
homePage = new HomePage();homePage.addProduct("Product_1");homePage.doLogout();loginPage.getContainer().shouldBe(visible);
Compare
![Page 24: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/24.jpg)
Three Chords of “Procedural” PageUtils :)
![Page 25: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/25.jpg)
1. Abstraction
Factor out implementation details into helper methods
doLogin(username, password);
public static void doLogin(String login, String password){ usernameField().setValue(login); passwordField().setValue(password); loginButton().click();}
public static SelenideElement usernameField(){ return $(By.name("username"));}
![Page 26: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/26.jpg)
2. Modularity
Collect your helpers in classes of correspondent context
![Page 27: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/27.jpg)
3. Try to Be “Functional”
– write functions returning result only based on passed parameters
– write smaller functions and use them in a 'chain':
select(dropdownIn(userPanel()), “En”)
– Instead of
selectLanguageDropdownInUserPanel(“En”)
– Use Composition over Inheritance
![Page 28: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/28.jpg)
P.S. Be smart ;)
– You can't use inheritance.● If you have any conventions you need to remember to
follow them
![Page 29: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/29.jpg)
When Use?
● Need to involve and teach Manual/Junior Automation QA
● Need a fast solution
● Language support Functional Paradigm
– At least first-class functions
● You know what you do:)
![Page 30: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/30.jpg)
When maybe not use?
● All committers to test framework are either Senior QA Automation or Developers
● No need to teach Manual QA/Juniors
● No tough deadlines
● Java (only)
![Page 31: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/31.jpg)
When not use?
● Your are Junior/Middle
– And/Or Manager/Lead/Dev says: OOP or DIE! :)
● You can't predict what features your framework may need in future
![Page 32: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/32.jpg)
This is how your test
model may look
![Page 33: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/33.jpg)
“What are those classes
in pagegetters package?”
:)
![Page 34: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/34.jpg)
Here comeLoadable Components...
public abstract class SimpleLoadableComponent { public void get() { try { isLoaded(); } catch (Error e) { load(); isLoaded(); } }
protected abstract void load();
protected abstract void isLoaded() throws Error;}
![Page 35: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/35.jpg)
What's the point?O_o
![Page 36: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/36.jpg)
From :(
Login.open(baseurl);
Home.open(username, password);
Home.ensureHasProduct("Product_1");
Product.open("Product_1");
ProductTestTables.open();
ProductTestTables.addCategoryButton().shouldBe(visible);
![Page 37: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/37.jpg)
Technically To
(new ProductTestTablesPage(
new ProductPage(
new HomePage(
new LoginPage(baseurl), username, password),
"Product_1"))).get();
![Page 38: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/38.jpg)
Actually To :)
ProductTestTables.page("Product_1").get();
![Page 39: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/39.jpg)
Selenium LoadableComponentpublic abstract class LoadableComponent<T extends LoadableComponent<T>> {
@SuppressWarnings("unchecked")
public T get() {
try {
isLoaded();
return (T) this;
} catch (Error e) {
load();
}
isLoaded();
return (T) this;
}
protected abstract void load();
protected abstract void isLoaded() throws Error;
}
![Page 40: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/40.jpg)
Ajax? > Selenium SlowLoadableComponent
Calm down, no code, just link:)
● (c) A LoadableComponent which might not have finished loading when load() returns. After a call to load(), the isLoaded() method should continue to fail until the component has fully loaded.
![Page 41: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/41.jpg)
Once you need some abstract classes to DRY your code...
public abstract class
AbstractPage<T extends SlowLoadableComponent<T>> extends SlowLoadableComponent<T>{
O_O
![Page 42: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/42.jpg)
Typical isLoaded() Implementations
![Page 43: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/43.jpg)
'url-based' isLoaded implementation
protected void isLoaded() throws Error {
String url = driver.getCurrentUrl();
assertTrue(url.contains(pageUrl));
}
![Page 44: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/44.jpg)
If you want to identify pages by actual content
protected void isLoaded() throws Error {
try {
WebElement div = driver.findElement(By.id("login-select"));
} catch (NoSuchElementException e) {
fail("Cannot locate user name link");
}
}
![Page 45: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/45.jpg)
Once you use @FindBy
public void isLoaded() throws Error {
if (loginButton != null) {
assertElementIsDisplayed(loginButton);
} else {
fail("Login button is not loaded");
}
}
![Page 46: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/46.jpg)
Typical Selenide isLoaded() implementation
public void isLoaded(){
Login.container().shouldBe(visible);
}
![Page 47: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/47.jpg)
Selenide LoadableComponent
public abstract class SelenideLoadableComponent {
public void get() {
long originalTimeout = Configuration.timeout; try {
Configuration.timeout = 0; isLoaded(); Configuration.timeout = originalTimeout; } catch (Error e) {
Configuration.timeout = originalTimeout; load(); isLoaded(); } } protected abstract void load(); protected abstract void isLoaded();}
“slow”,ajax-friendly by default
“no” <T extends Madness>
![Page 48: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/48.jpg)
If you wish...
public abstract class AbstractPage extends
SelenideLoadableComponent {
public abstract void isLoaded();
}
Home.page().get();
doCrazyStuff();
Home.page().isLoaded() // still?
![Page 49: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/49.jpg)
Implementation Example
![Page 50: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/50.jpg)
Initialize
public class ProductPage extends SelenideLoadablePage{
private HomePage parent;
protected String productName;
public ProductPage(HomePage parent, String productName){
this.parent = parent;
this.productName = productName;
}
….
![Page 51: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/51.jpg)
Load
protected void load() {
parent.get();
Home.ensureHasProduct(productName);
Product.open(productName);
}
![Page 52: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/52.jpg)
isLoaded()
public void isLoaded() {
Breadcrumb.productLink(productName).shouldBe(visible);
Product.testTableItem().shouldBe(visible);
}
![Page 53: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/53.jpg)
Factory
public class Product {
public static ProductPage page(String productName){
return new ProductPage(Home.page(), productName);
}
…}
![Page 54: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/54.jpg)
If it would be so “simple”...
![Page 55: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/55.jpg)
But it would not :ppublic class AuthorizedPage extends AbstractPage {
protected AbstractPage parent;
private String username; private String password;
public AuthorizedPage( LoginPage parent, String username, String password) {
this.parent = parent; this.username = username; this.password = password; }
public AuthorizedPage(AuthorizedPage parent){
this.parent = parent; } ...
![Page 56: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/56.jpg)
Initializepublic class ProductPage extends AuthorizedPage{
protected String productName;
public ProductPage(HomePage parent, String productName){
super(parent);
this.productName = productName;
}
public ProductPage(ProductPage parent){ //It's possible to “load” page from itself
super(parent);
this.productName = parent.getProductName();
}
….
![Page 57: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/57.jpg)
Load protected void load() {
parent.get();
if (parent instanceof ProductPage) {
Breadcrumb.productLink(((ProductPage) parent).getProductName()).click();
} else { //parent instanceof HomePage
Home.ensureHasProduct(productName);
Product.open(productName);
}
}
![Page 58: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/58.jpg)
Though...The beast is not so scary after you write up to 10 first Lions Components :)
![Page 59: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/59.jpg)
AndYou still can live only with PageUtils and keep LoadableComponents as options to be implemented by crazy devs:)
QA Dev
![Page 60: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/60.jpg)
scenario "Surf Pages", { where "Page is #page", { page = [ Login.page(), Home.page(), Settings.page(), Login.page(Authorized.page()), Product.page(TEST_PRODUCT), Login.page(Authorized.page()), Settings.page(), Product.page(Home.page(Settings.page()), "Product_1"), Product.page(Product.page(TEST_PRODUCT)), ProductTestTables.page(TEST_PRODUCT), Login.page(Authorized.page())] } then "go to #page", { page.get() }}
Bonus :)
![Page 61: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/61.jpg)
When Maybe Use?
● No confident urls
● Complex “deep” page hierarchy
● Authorization > Menus > SubMenus > Tabs > Extra > Extra...
![Page 62: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/62.jpg)
When Use?
● Desired dependent End to End scenarios with “walking through pages” feature
– emulating real user experience
– big amount of such test cases
![Page 63: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/63.jpg)
When maybe not use?
● Too many ways to load the same page
● Though you still can implement LC for 1 way, if you need to use it often.
● Too many pages, especially “visible” at the same time
![Page 64: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/64.jpg)
When not use?
● URL-based loading is enough
– Or work around via custom JS load helpers is enough● what is true for most cases...
● Have no “deep” pages
![Page 65: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/65.jpg)
All Together
![Page 66: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/66.jpg)
● PageUtils:
class Login
– Page smart loader:
Login.page().get()
– Page root html element:
Login.container()
– Method to open Page once preconditions satisfied:
Login.open()
– Page elements:
Login.signInButton()
– Page business steps:
Login.doLogin(“guest”, “1234”)
● LoadableComponent:
class LoginPage
isLoaded()
load()
Conventions
![Page 67: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/67.jpg)
Ideas to think about
![Page 68: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/68.jpg)
LoadableComponent
● Is not PageObject
– Though you can integrate it into PageObject, violating Single Responsibility principle
● It's an object incapsulating page loading logic.
– Initializing the “loading way” through LC constructor● It's possible also to move logic into separate loadable
components fro each “way”, though this may lead to overcomplicated hierarchy
– choosing the “way” in load() implementation
– And then just get() your page
![Page 69: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/69.jpg)
LoadableComponent Integration
● PageUtils + LoadableComponent
– Two classes instead of one
● PageObject + LoadableComponent
– May be harder to achieve friendly tests code
● PageObject extends LoadableComponent
– Bulky
– Harder to explain to juniors/interns
– Violates Single Responsibility Principle
![Page 70: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/70.jpg)
LoadableComponent Factory
Too more calls to page() ?
Use Singleton Pattern
![Page 71: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/71.jpg)
PageUtils
Page elements as functions public static SelenideElement usernameField(){
return $(By.name("username"));
}
…
Login.usernameField().setVal("guest");
Page elements as locators public static final By usernameField = By.name("username");
…
$(Login.usernameField).setVal("guest");
Compare
![Page 72: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/72.jpg)
Functional “Scales”
● Main cons of Procedural approach is that it may be not DRY
● In most cases you can fix this with high-order functions in much more concise way than with OOP
– Though less obvious for non-FP user
![Page 73: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/73.jpg)
Ideas for improvements● Use Groovy as main language
– in order to simplify implementation.
– Finally Java is the OOP language● and not adapted for both procedural and functional styles.
– In Groovy OOP may be not “bulky”
● and with some functional & metaprogramming features you can achieve the same level of simplicity still powerful
– and easy to explain to juniors “how to use” (though not “how to understand details”)
![Page 74: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/74.jpg)
Did it work for Manual QA?
![Page 75: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/75.jpg)
Demo
![Page 76: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/76.jpg)
Q&A
![Page 77: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/77.jpg)
Resources, Links
● Src of example test framework:
https://github.com/yashaka/gribletest
● Programming Paradigms Comparison: https://bitbucket.com/yashaka/oopbucket/src
● Functional Thinking articles: http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=functional+thinking
● Application under test used in easyb examples: http://grible.org/download.php
● Instruments
– http://selenide.org/
– http://code.google.com/p/selenium/wiki/LoadableComponent
![Page 78: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/78.jpg)
● To Artem Chernysh for implementation of main base part of the test framework for this presentation
– https://github.com/elaides/gribletest● To Maksym Barvinskyi for application under test
– http://grible.org/
![Page 79: Three Simple Chords of Alternative PageObjects and Hardcore of LoadableComponents](https://reader033.vdocument.in/reader033/viewer/2022052504/554a19b1b4c9055c598b530f/html5/thumbnails/79.jpg)
Contacts
● skype: yashaolin
● twitter: @yashaka
● http://www.linkedin.com/in/iakivkramarenko