February 4, 2017

Page Object Model

Introduction

Page Object Model Framework has now a days become very popular test automation framework in the industry and many companies are using it because of its easy test maintenance and reduces the duplication of code.
The main advantage of Page Object Model is that if the UI changes for any page, it don’t require us to change any tests, we just need to change only the code within the page objects (Only at one place)
Public class FacebookSignInPage
{
                private WebDriver driver;
                private By TXT_UserName = By.id("fbUserName");
                private By TXT_Password = By.id("fbpassWD");
                Private By BTN_LogIN = By.id("signIn");
}
 In the above example, we have first identified the locators and defined it on the top of the class. In this way we can achieve readability of test scripts and we can easily identify locators and change them if needed only at only one place.
Page Object model is writing all the functionalities / reusable components of a page that we want to automate in a separate class.
Let’s say we have four pages in Facebook application as Home page, Login page, Create Account and Forgot password page etc. For all these pages we will create classes as HomePage.java, LoginPage.java, CreateAccountPage.java and ForgotPasswordPage.java. In each class we will identify and write reusable methods which are specific to a page.
Here in the first page ‘ home page' which will have many options like Search, NewsFeed,Profile and Notifications etc., links. Based on the user action it navigates to respective page. Now all functionalities that we want to automate should have reusable methods/components for each page.
 “Whenever we are navigating to other page, we need to return that page object, else return the current page object as this action doesn't navigate to other page represented by another Page Object.”

The Page Object model provides the following advantages.

  1.  There is clean separation between test code and page specific code such as locators and layout.
  2. There is single repository for the services or operations offered by the page rather than having these services scattered throughout the tests.
In both cases this allows any modifications required due to UI changes to all be made in one place.
Let’s take one simple example which illustrate the POM
public void loginTestCase()
{
                driver.navigate().to(“https://facebook.com”);
                driver.findElement(By.id("fbUserName ")).sendKeys("UserName");
                driver.findElement(By.id("fbpassWD ")).sendKeys("Password");
                driver.findElement(By.name("signIn")).click();
}
If you observe the above function, there is no separation of test and test locators. If this is the case, in future if the UI changes, it must be changed in multiple places. It will also become difficult to identify where these locators are used as the chances of locators are being used in multiple tests are more.
We will try to rewrite the same example by implementing the page object model
public void loginTestCase()
{
                // To go to home page
                loginPage.gotoLoginPage();       

                //Login to the account
                loginPage.userLogin(username,password)

                //To click on SignIn link
                homePage = loginPage.clickOnSignIn()
                
                //To verify if user is navigated to Home page after clicking on SignIn Button
                Assert.assertTrue(homePage.verifyPage());
}
In the above function, we have not used any locators. It is completely separated by findElement, waits, exceptions and no static values in the code etc., we will be working only with the methods which are defined in multiple pages. Based on test, we will navigate to the required page and access those page methods.
Let’s get into POM to understand clearly.
Let’s discuss on the same facebook application with the pages Base page, Sigin page and CreateAccount page.

Step 1:

This the main class for page object model, where we will initialize WebDriver object based on the browser type passed as a parameter in textng.xml file. We will also need to pass the base page application URL in testng.xml as parameter.
In this example we have taken two browsers Firefox and chrome. To initiate Chrome browser, we need to set the System Property by providing the chromedriver executable file path.
We need to create a method to return Webdriver 'driver' where this is used for test execution.
Below is the example TestBaseSetup.class file
package com.pack.base;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
public class TestBaseSetup
{
                private WebDriver driver;
                static String driverPath = "D:\\chromedriver\";

                public WebDriver getDriver()
                {
                                return driver;
                }

                private void setDriver(String browserType, String appURL)
                {
                                switch (browserType)
                                {
                                                case "chrome":
                                                                driver = initChromeDriver(appURL);
                                                                break;
                                                case "firefox":
                                                                driver = initFirefoxDriver(appURL);
                                                                break;
                                                default:
                                                                System.out.println("browser : " + browserType+ " is invalid, Launching Firefox as browser of choice..");
                                                                driver = initFirefoxDriver(appURL);
                                }
                }

                private static WebDriver initChromeDriver(String appURL)
                {
                                System.out.println("Launching google chrome with new profile..");
                                System.setProperty("webdriver.chrome.driver", driverPath+ "chromedriver.exe");
                                WebDriver driver = new ChromeDriver();
                                driver.manage().window().maximize();
                                driver.navigate().to(appURL);
                                return driver;
                }
 
               private static WebDriver initFirefoxDriver(String appURL)
                {
                                System.out.println("Launching Firefox browser..");
                                WebDriver driver = new FirefoxDriver();
                                driver.manage().window().maximize();
                                driver.navigate().to(appURL);
                                return driver;
                }


                @Parameters({ "browserType", "appURL" })
                @BeforeClass
                public void initializeTestBaseSetup(String browserType, String appURL)
                {
                                try
                                {
                                                setDriver(browserType, appURL);
                                }
                                catch (Exception e)
                                {
                                                System.out.println("Error: " + e.getMessage());
                                }
                }

                @AfterClass
                public void tearDown()
                {
                                driver.quit();
                }
}

Step 2:

Now we will create Page Object classes. For each page we will create a separate class with constructor. We identify the locators and keep all together on just below the class. This will help us to re-use the locators for multiple methods and the main important is, we can easily find the locator and change if required.
We need to identify and list of all the possible functionalities on this page and we should write methods in such a way they are re-used. Whenever there is a method to click on a button or link, we should return driver object of that page.
We will look into the below example. For method SignInBtn, we are returning after clicking on Sign In button as it is redirecting to sign in page.

BasePage.class

package come.pack.common.pageobjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class BasePage
{
                protected WebDriver driver;
                private By signInButton = By.linkText("Sign in");       

                public BasePage(WebDriver driver)
                {
                                this.driver = driver;
                }               

                public SignInPage clickSignInBtn()
                {
                                System.out.println("clicking on sign in button");
                                WebElement signInBtnElement=driver.findElement(signInButton);
                                if(signInBtnElement.isDisplayed()&&signInBtnElement.isEnabled())
                                                signInBtnElement.click();
                                else
                                                System.out.println("Element not found");
                                return new SignInPage(driver);
                }               

                public String getPageTitle(
                {
                                String title = driver.getTitle().toString();
                                return title;
                }               

                public boolean verifyBasePageTitle()
                {
                                String expectedPageTitle="Facebook";
                                return getPageTitle().equals(expectedPageTitle);
                }
}
In the below SignInpage.class, we have methods to click on create account and sign in button. We are just verifying an error in sign in page for now in this example.

SignInPage.class

package come.pack.common.pageobjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

Public class FacebookSignInPage
{
             private WebDriver driver;
private By headerPageText = By.id(“header ");
             private By TXT_UserName = By.id("fbUserName");
             private By TXT_Password = By.id("fbpassWD");
             Private By BTN_LogIN = By.id("signIn");

            private By createAccountLink = By.id("signup"); 
            private By errorMsgTxt = By.id("worningMessage ");
                         public FacebookSignInPage (WebDriver driver)
             {
                               this.driver=driver;
             }

             p
ublic String getSignInPageTitle()             {
                          String pageTitle = driver.getTitle();
                          return pageTitle;
             }
             public boolean verifySignInPageTitle() 
            {
                          String expectedTitle = "Facebook";
                          return getSignInPageTitle().contains(expectedTitle);
             }           

             public boolean verifySignInPageText() 
            {
                           WebElement element = driver.findElement(headerPageText);
                           String pageText = element.getText();
                           String expectedPageText = "Facebook";
                           return pageText.contains(expectedPageText);
             }

             public CreateAccountPage clickonCreateAnAccount() 
             {                                       WebElement element=driver.findElement(createAccountLink);
                          if(element.isDisplayed()&&element.isEnabled())
                                       element.click();
                          return new CreateAccountPage(driver);
             }

              pulbic boolean verifySignIn() 
             {
                          enterUserName("UserName ");
                          enterPassword("PassWord");
                          clickOnSignIn();
                          return getErrorMessage().contains("incorrect");
             }
             public void enterUserName(String userName) 
             {
                          WebElement emailTxtBox = driver.findElement(emailTextBox);
                          if(emailTxtBox.isDisplayed())
                                       emailTxtBox.sendKeys(userName);
             }
                          public void enterPassword(String password)
              {
                          WebElement passwordTxtBox = driver.findElement(passwordTextBox);
                          if(passwordTxtBox.isDisplayed())
                                       passwordTxtBox.sendKeys(password);
             }
             public void clickOnSignIn() 
             {
                          WebElement signInBtn = driver.findElement(loginBtn);
                          if(signInBtn.isDisplayed())
                                       signInBtn.click();
             }

             public String getErrorMessage()
             {
                          String strErrorMsg = null;
                          WebElement errorMsg = driver.findElement(errorMsgTxt);
                          if(errorMsg.isDisplayed()&&errorMsg.isEnabled())
                                       strErrorMsg = errorMsg.getText();
                          return strErrorMsg;
             }
}

CreateAccountPage.class

package come.pack.common.pageobjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class CreateAccountPage 
{
                private WebDriver driver;
                private By headerPageTxt = By.id("header");
                public CreateAccountPage(WebDriver driver)
                {
                                this.driver=driver;
                }
                
                public String getPageTitle()
                {
                                String title = driver.getTitle();
                                return title;
                }
                
                public boolean verifyPageTitle()
                {
                                String pageTitle = "Create your Google Account";
                                return getPageTitle().contains(pageTitle);
                }

                public boolean verifyCreateAccountPageText()
                {
                                WebElement element = driver.findElement(headerPageTxt);
                                String pageText ="Create your Google Account";
                                return element.getText().contains(pageText);
                }
}

Step 3:

Now we will see how to write Tests for the above pages. For all the page Objects, we will now create Tests for each page. This will help us in easy maintainability. We will see first example test for "BasePageTest.class".
In the below example we verifying home page by taking the title. We have written a method in BasePage and we are just calling it here.
package com.pack.common.tests;

import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.pack.base.TestBaseSetup;
import come.pack.common.pageobjects.BasePage;

public class BasePageTest extends TestBaseSetup
{
                private WebDriver driver;
                
                @BeforeClass
                public void setUp()
  {
                                driver=getDriver();
                }
                
                @Test
                public void verifyHomePage()  
  {
                                System.out.println("Home page test...");
                                BasePage basePage = new BasePage(driver);
                                Assert.assertTrue(basePage.verifyBasePageTitle(), "Title doesn't match");
                }
}
 We will look into the next test 'SignInPageTest'. In the below test, we are verifying page title, page text, and Sign in functionality. For all these verifications we have defined methods in SignInPage class which we are calling from tests.
package com.pack.common.tests;

import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.pack.base.TestBaseSetup;
import come.pack.common.pageobjects.BasePage;
import come.pack.common.pageobjects.SignInPage;

public class SignInPageTest extends TestBaseSetup
{

                private WebDriver driver;
                private SignInPage signInPage;
                private BasePage basePage;
            
                @BeforeClass
                public void setUp()
                {
                                driver=getDriver();
                }
                                
                @Test
                public void verifySignInFunction()
                 {
                                System.out.println("Sign In functionality details...");
                                basePage = new BasePage(driver);
                                signInPage = basePage.clickSignInBtn();
                                Assert.assertTrue(signInPage.verifySignInPageTitle(), "Ttitle doesn't match");
                                Assert.assertTrue(signInPage.verifySignInPageText(), "Page text not matching");
                                Assert.assertTrue(signInPage.verifySignIn(), "Unable to sign in");
                 }
}
  Now the create test 'CreateAnAccountTest'. Now we should be able to understand the verification that we are doing in the below test.
package com.pack.common.tests;
 
import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.pack.base.TestBaseSetup;
import come.pack.common.pageobjects.BasePage;
import come.pack.common.pageobjects.CreateAccountPage;
import come.pack.common.pageobjects.SignInPage;

public class CreateAnAccounTest extends TestBaseSetup
 {
                private WebDriver driver;
                private SignInPage signInPage;
                private BasePage basePage;
                private CreateAccountPage createAccountPage;
                               
                @BeforeClass
                public void setUp()
                 {
                                driver=getDriver();
                }
                                
                @Test
                public void verifyCreateAnAccounPage()
                 {
                                System.out.println("Create An Account page test...");
                                basePage = new BasePage(driver);
                                signInPage = basePage.clickSignInBtn();
                                createAccountPage = signInPage.clickonCreateAnAccount();
                                Assert.assertTrue(createAccountPage.verifyPageTitle(), "Title not matching");
                                Assert.assertTrue(createAccountPage.verifyCreateAccountPageText(), "Ttext not matching");
                }
}

Step 4:

We are done with Base Setup, Page Objects creation, Tests creation. Now we will execute them using 'testng.xml' file. We will add the classes which we want to test.
Remember we need to pass the parameters 'browserType' and 'appURL' for the base Setup class.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Page Object test example">
                <parameter name="appURL" value="https://www.facebook.co.in/"/>
                <parameter name="browserType" value="chrome"/>
                <test name="sample test">
                                <classes>
                                                <class name="com.pack.common.tests.HomePageTest"/>
                                                <class name="com.pack.common.tests.SignInPageTest"/>
                                                <class name="com.pack.common.tests.CreateAnAccounTest"/>
                </classes>
                </test>
</suite>

January 26, 2017

Locators in Selenium and How to Use


Selenium WebDriver uses 8 locators to find the elements on web page. The following are the list of locators supported by selenium.

I’ve listed all locators to be used when scripting.

Locator
Description
Usage
Id
Select the Element using @id attribute
By.id(<id of the tag>)
Name
Select first Element with the specified @name attribute
By.name(<name of the tag>)
ClassName
Select first element with the specified @class attribute
By.className(<class of the tag>)
LinkText
Select anchor tag which matches the text specified
By.linkText(“<text of the link”);
partialLinkText
Select anchor tag which contains the text specified
By. partialLinkText (“<text of the link”);
tagName
Select the first element with the specified tag
By.tagName(<tag name>)
cssSelector
Select the element using the CssSelector Provided
By.cssSelector(“cssSelector”)
xPath
Select the element using the xpath expression
By.xPath(“xpath of the element”)

Locating an Element by ID:

The most efficient way and preferred way to locate an element on a web page is by ID. ID will be the unique on web page which can be easily identified.
IDs are the safest and fastest locator option and should always be the first choice even when there are multiple choices, it is like an unique identification number.
Examples:
  1. <div id="mainMenu">menu items</div>
  2. <input id="email" class="emailAddress" type="text"/>

How?
  • WebElement Ele = driver.findElement(By.id("mainMenu "));

Unfortunately, there are many cases where an element does not have a unique id (meaninge ids are being dynamically generated). In these cases, we need to choose an alternative locator strategy, however if possible we should ask development team of the web application to add few ids to a page specifically for automating the application.

Locating an Element by Name:

When there is no Id to use, the next worth seeing if the desired element has a name attribute. But make sure there the name cannot be unique all the times. If there are multiple names, Selenium will always perform action on the first matching element
Examples:
  1. <input name="login" class="loginClass" type="submit"/>
  2. <label name="login">

How?
  • WebElement loginButton= driver.findElement(By.name("login"));

As I said, from the above example if you observe both label and input tags are having the same value for ‘Name’ attribute. If you try to find the element using name locator, input tag will be considered as it is the first tag which matches the specified name

Locating an Element by LinkText:

Finding an element with link text is very simple. But make sure, there is only one unique link on the web page. If there are multiple links with the same link text (such as repeated header and footer menu links), in such cases Selenium will perform action on the first matching element with link.
Examples:
  1. <a href="/html/default.asp">Learn HTML</a>

How?
  • WebElement linkHtml = driver.findElement(By.linkText("Learn HTML "));

Locating an Element by Partial LinkText:

In the same way as LinkText, PartialLinkText also works in the same pattern, but the only difference here is you not required provide exact name of the link.
Examples:
  1. <a href="/html/default.asp">Learn HTML</a>
  2. <a href="/css/default.asp">Learn CSS</a>

How?
  • WebElement download = driver.findElement(By.PartialLinkText("Learn"));

Locating an Element by TagName:

TagName can be used with Group elements like, Select and check-boxes / dropdowns.
Examples:
                <select name=’Weekends’>
                        <option>Saturday</option>
     <option>Sunday</option>
                </select>
How?
  • WebElement selectTag = driver.findElement(By.tagName("select"));

Locating an Element by Class Name:

There may be multiple elements with the same name, if we just use findElementByClassName,m make sure it is only one. If not the you need to extend using the classname and its sub elements.
Example:
  1. <input name="login" class="loginClass" type="submit"/>

How?
  • WebElement loginClass =driver.findElement(By.className(“loginClass”));

Locating an Element by CSS Selector:

CSS mainly used to provide style rules for the web pages and we can use for identifying one or more elements in the web page using css.
cssSelector is faster than xpath expression. We can you use Css Selectors to make sure scripts run with the same speed in IE browser. CSS selector is always the best possible way to locate complex elements in the page.
Important point in cssSelector:
  1. Immediate child is defined as > Ex: div>input
  2. Child of child is defined as white space Ex: div input
  3. Id will be represented as # Ex: input#loginTest
  4. Class will be represented as dot(.) Ex: input.loginClass
  5. Next sibling will be represented as +
  6. Attribute Values will be represented as input[name=’sample’]
  7. String matchers
    1. Equals: input[id=’user_name’]
    2. Starts with: Input[id^=’user_’]
    3. Ends with: Input[id$=’_name’]
    4. Sub string: input[id*=’ser_na’]
  8. Inner Text will be represented as input.contains(‘mailAddre’)

How?:
  • WebElement emailAddress = driver.findElements(By.cssSelector("input[id=’email']"));

Locating an Element by XPath:

XPath is designed to allow the navigation of XML documents, with the purpose of selecting individual elements, attributes, or some other part of an XML document for specific processing
There are two types of xpath
  • Native Xpath, it will start from the root tag normally it is html
    • Here the advantage of specifying native path is, finding an element is very easy as we are mention the direct path. But if there is any change in the path (if something has been added/removed) then that xpath will break.

  • Relative Xpath, it we will be the relative path, it is like we will tell the xpath to find an element by telling the path in between.
    • Advantage here is, if at all there is any change in the html that works fine, until unless that particular path has changed. Finding address will be quite difficult as it need to check each and every node to find that path.

Example:
<html>
    <head>
        <title>…</title>
    </head>
    <body>
       <div>
          <input>…</input>
          <span>…</span>
       </div>
    </body>
</html>
How?
  • Native xpath = html/body/div/input
  • Relative xpath = //div/input

Other Example:
  1. Image: //img[@alt='image alt text goes here']
  2. Table: //table[@id='table12']/td[4]
  3. Anchor Tag:  //a[contains(@href,'href goes here')]
  4. Input: /input[@name='name2' and @value='yes']
Please find the above information in the attached file Locators