Selenium Hangout 5 Recap

Official Selenium Blog


0:00 – 01:10 Intro

1:11 – 13:00 WebDriver W3C Spec & Selenium 3 Update

  • Progress on the spec, still a work in progress
  • No user facing changes to the Selenium API as a result
  • Trying to pair the spec and Selenium 3 together
  • If all goes well, the spec and Selenium 3 could drop during Selenium Conf (fingers crossed)

13:01 – 24:10Selenium Conf 2014 Update

24:11 – 39:00 Discussion about 5 Hidden Costs Of Selenium Whitepaper from Telerik

View original post

Configuring the Android Development Kit

Android Development Kit (ADT)

The Android SDK provides the API libraries and developer tools necessary to build, test, and debug apps for Android.  The ADT can be setup using either of the following option:

Option1 (Using the Eclipse IDE preconfigure with the ADT)

  1. Download the Android SDK from http://developer.android.com/sdk/index.html depending on the architecture of your system
  2. Unzip the folder adt-bundle-windows-xxx-yyyymmdd
  3. Open the adt-bundle-windows-xxx-yyyymmdd folder and navigate to …\adt-bundle-windows-x86-20131030\eclipse
  4. Launch the Android Developer Tools IDE by double clicking on the eclipse.exe

Note: If the following error is encountered while running the Eclipse

Untitled

Open eclipse.ini in a notepad

Untitled

and modify the value of -XX:MaxPermSize=1024M to -XX:MaxPermSize=512M.

If the value 512M does not work out then further reduce the value to 256M i.e. to -XX:MaxPermSize=256M

Untitled

Option2 (Using the ADT Plugin for Eclipse)

Android offers a custom plugin for the Eclipse IDE, called Android Development Tools (ADT). This plugin provides a powerful, integrated environment in which to develop Android apps.

Download the ADT Plugin

  1. Start Eclipse, then select Help > Install New Software.
  2. Click Add, in the top-right corner.
  3. In the Add Repository dialog that appears, enter “ADT Plugin” for the Name and the following URL for the Location: https://dl-ssl.google.com/android/eclipse/
  4. Click OK
  5. If you have trouble acquiring the plugin, try using “http” in the Location URL, instead of “https” (https is preferred for    security reasons)
  6. In the Available Software dialog, select the checkbox next to Developer Tools and click Next.
  7. In the next window, you’ll see a list of the tools to be downloaded. Click Next.
  8. Read and accept the license agreements, then click Finish.
  9. If you get a security warning saying that the authenticity or validity of the software can’t be established, click OK.
  10. When the installation completes, restart Eclipse.

Configure the ADT Plugin

Once Eclipse restarts, you must specify the location of your Android SDK directory:

  1. In the “Welcome to Android Development” window that appears, select Use existing SDKs.
  2. Browse and select the location of the Android SDK directory you recently downloaded and unpacked.
  3. Click Next.

Launching the Android Emulator

 To create an AVD:

  1. In Eclipse:  select Window > Android Virtual Device Manager, or click the AVD Manager icon in the Eclipse toolbar

Untitled

2.   Create an AVD by clicking on New button in the Android Virtual Device Manager

Untitled

3.  Fill in the details for the AVD in the Create New AVD dialog appears.

Untitled

4.   Click OK. There will be an entry visible in the Android Virtual Device Manager with                     the AVD Name as MyFirstAVD

Untitled

5.   Click on the Start.. button after selecting the AVD from the the Android Virtual Device               Manager

Untitled

6.   Click on the Launch button in the Launch Options window

Untitled

7.   Wait for the emulator to complete loading

Untitled

8.    Below is the AVD console(MyFirstAVD) listening on Port:5554

Untitled

Congratulations!  The AVD is up and running. It can now be used for testing the mobile apps.

Setting up the Environment Variables

    1. Right click on Computer in the desktop start menu and select Properties from the resulting menu.
    2. In the properties panel, select the Advanced System Settings link and, in the resulting dialog, click on the Environment Variables… button.
    3. In the Environment Variables dialog, locate the Path variable in the System variables list, select it and click on Edit…. Locate the end of the current variable value string and append the path to the android platform tools to the end, using a semicolon to separate the path from the preceding values. For example, assuming the ADT bundle was installed into /Users/demo/adt-bundle-windows-x86_64, the following would be appended to the end of the current Path value:

;C:\Users\demo\adt-bundle-windows-x86_64\sdk\platform-tools;C:\Users\demo\adt-bundle-windows-x86_64\sdk\tools

                 4.     Click on OK in each dialog box and close the system properties control                                             panel.Once  the above steps are complete, verify that the path is correctly                                       set by opening a  Command Prompt window (Start -> All Programs ->                                                 Accessories -> Command Prompt) and at the prompt enter:

echo %Path%
                5.     The returned path variable value should include the paths to the Android SDK                             platform tools folders. Verify that the platform-tools value is correct by                                             attempting to run the adb tool as follows:
adb
                6.    The tool should output a list of command line options when executed.
                7.    Similarly, check the tools path setting by attempting to launch the Android SDK                          Manager:
android
                8.   In the event that a message similar to following message appears for one or                                  both of the commands, it is most likely that an incorrect path was appended to                            the Path environment variable:
         'adb' is not recognized as an internal or external command, operable program or batch file.
               9.    Set the Android path to the directory where you have the android sdk installed:
                       ANDROID_HOME=C:\< installation location>\sdk

Calabash: Functional Testing For Mobile Apps

Calabash is an automated testing technology for Android and iOS native and hybrid applications. Calabash is a free open source project, developed and maintained by Xamarin.
While Calabash is completely free, Xamarin provides a number of commercial services centered around Calabash and quality assurance for mobile, namely Xamarin Test Cloud consisting of hosted test-execution environments which let you execute Calabash tests on a large number of Android and iOS devices.

How Calabash works in Android:
When a Calabash Android test is executed both your local computer and a device is involved. The device might be an emulator or an actual physical device. The setup looks like this:

5

Features: The feature files describe the user-stories you want to test. You can test one or more features in one test run.

Step Definitions: Calabash Android comes with a set of predefined step which you can find here. These steps are generic and made to get you up and running fast. When you get more into Calabash you can implement your own custom steps that use the business terms your project uses like I transfer money to my spending’s account or I play “Never Gonna Give You Up”.

Your app: You don’t have to make modifications to your app before testing it.

Instrumentation Test Server: This is another app that will be installed and executed the device. This app is based on ActivityInstrumentationTestCase2 from the Android SDK. It is generated by the Calabash Android framework.

Calabash-android Setup

The First and foremost things is to have all the prerequisites tools installed in your system. The below are the step by step explanation of how to quickly setup your development environment for functional testing of mobile apps (android) using Cucumber and Calabash-android.

Pre-Requisites:

  1. You need to have Ruby installed. Verify your installation by running ruby -v in a terminal – it should print “ruby 1.8.7” (or higher).
    If you are on Windows you can get Ruby from RubyInstaller.org
    http://rubyinstaller.org/downloads/

6       2.    During installation check the 2nd and 3rd options i.e.

  • Add Ruby executables to your PATH
  • Associate .rb and .rbw files with this Ruby installation

7

  1. You should have the Android SDK installed and the environment variable ANDROID_HOME should be pointing to it. Refer here
  2. Install Cucumber
    Navigate to Command Prompt and type “gem install cucumber
  3. Install Calabash-android
    Navigate to Command Prompt and type “gem install calabash-android

Automating an application using Calabash

  • Make sure that the android emulator is running. Refer here.
  • Create a folder on the desktop (e.g. testapp)
  • Download a test app from the internet and place it in the “testapp” folder
  • For this example I have used the GO Contacts EX.apk downloaded from http://www.appsapk.com/go-contacts-ex/
  • Open the command prompt and navigate to the “testapp” folder8
  • Type  “calabash-android gen” and hit enter twice9
  • features folder will automatically get created in the “testapp” folder10
  • The features folder consists of the following file and directories
    • step_definitions (folder)
    • support (folder)
    • my_first.features (.feature file)
  • 11
  • Edit “my_first.feature” file and replace the content with the following BDD statements

Feature:  As a user of GO Contacts EX.apk
I want to verify the addition and deletion of contacts in the application
Scenario: As a valid user I can add a friend into the contacts list
Given I press button with id “dial_btn_num9”
Given I press button with id “dial_btn_num9”
Given I press button with id “dial_btn_num7”
Given I press button with id “dial_btn_num5”
Given I press button with id “dial_btn_num3”
Given I select new contacts with id “newnumber_title”
Given I should wait for the text “New contact” to appear
When I enter the text “myfirstnumber” into input field number 1
When I enter the text “testnum” into input field number 2
* I press button with id “btn_done”
* I should wait for the text “No Record” to appear
Then I should see the text “No Record”
Then the expected value “No Record” should be equal to the actual value

“my_first.feature” file will now look something like this:

12

  • Edit the step_definitions folder and edit the calabash_steps.rb ruby file (open in a notepad)

Replace the content of the file with the following code:

When /^I enter the text “([^\”]*)” into input field number (\d+)$/ do |text, number|
performAction(‘enter_text_into_numbered_field’,text, number)
end

Given /^I press button with id “([^\”]*)”$/ do |button_id|
performAction(‘click_on_view_by_id’,button_id)
end

Given /^I select new contacts with id “([^\”]*)”$/ do |view_id|
performAction(‘click_on_view_by_id’,view_id)
end

Given /^I should wait for the text “([^\”]*)” to appear$/ do |text|
performAction(‘wait_for_text’, text)
end

Then /^I should see the text “([^\”]*)”$/ do |text|
performAction(‘assert_text’, text, true)
end

Then /^the expected text “([^\”]*)” should be equal to the actual text$/ do | expected_value |
actual_value =  performAction(‘get_text_by_id’,”recentemppty”)[‘message’]
raise “The current value is #{actual_value}” unless( actual_value == expected_value)
end

“calabash_steps.rb” ruby file will now look something like this:

13

  • Navigate to Command Prompt and type calabash-android resign “GO Contacts EX.apk”
  • 14
  • Next type calabash-android run “GO Contacts EX.apk” in the command prompt

Note: Make sure that the mobile emulator is unlocked, before running the test cases

Calabash-Android Pros and Cons

Pros

  • It is an Open-Source Tool – No Licensing Fees.
  • Since – The calabash-android is based on the Cucumber framework. The test cases can be easily created in real simple language.
  • Support for all the basic events and movements on the mobile are present in the libraries.
  • It has a thriving forum and Google Group: “Calabash Android”.

Cons

  • It takes time to run on an emulator or device as it always installs the app first before starting the first scenario.
  • If a step fails then the subsequent tests in the scenario are skipped
  • It is still in its nascent stage. Support for many complex scenarios or events is not supported. For that either you have to code your way in Ruby or wait for these supports to appear on the scene.
  • We must have the code of the app for identifying the ids of various elements.

Appium: Mobile App Automation Made Awesome

Appium is an open source test automation tool developed and supported by Sauce Labs to automate native and hybrid mobile apps. It uses JSON wire protocol internally to interact with iOS and Android native apps using the Selenium WebDriver.

Automating hybrid and native mobile applications for Android and iOS is a key function handled by Appium, a node.js server. One of the core tenets of Appium is that test codes can be written in any framework or language like Ruby on Rails, C# and Java without having to modify the apps for automation purposes. The interaction between node.js server and Selenium client libraries is what ultimately works together with the mobile application. Appium is open source and can seamlessly run on a variety of devices and emulators making it an apt choice for mobile test automation.

At present Appium only supports Android and iOS based apps but support for Firefox mobile OS is in pipeline.

How Appium works in Android:

Appium runs on real devices and emulators. It takes the Selenium commands from your test code and translates them into a readable format for UIAutomator, using the WebDriver JSON Wire Protocol. UIAutomator is Android’s native UI automation framework which supports running JUnit test cases directly in to the device from the command line. It uses java as a programming language but Appium will make it run from any of the WebDriver supported languages.

1

Android Requirements

  • Android SDK API >= 16 (SDK < 16 in Selendroid mode)
  • Mac OSX 10.7+ or Windows 7+ or Linux

Appium Setup

Download and Launch the Appium Server

      1. Download Appium for windows from https://github.com/appium/appium/releases (AppiumForWindows-x.xx.x.zip)
      2. Unzip the AppiumForWindows-x.xx.x folder
      3. Open the Appium interface by double clicking on the Appium.exe in the Appium folder 
      4. Run the server by clicking on the Launch button in the Appium interface

2                                5.   Command prompt with the following message will be displayed-info:         Welcome to Appium v x.xx.x

3

Desired Capabilities

Not all server implementations will support every WebDriver feature. Therefore, the client and server should use JSON objects with the properties listed below when describing which features a user requests that a session support. If a session cannot support a capability that is requested in the desired capabilities, no error is thrown; a read-only capabilities object is returned that indicates the capabilities the session actually supports

· Appium Sever Capabilities 

Capability

Description

Values

App The absolute local path or remote http URL to an .ipa or .apkfile, or a .zip containing one of these. Appium will attempt to install this app binary on the appropriate device first. Can also be one of chrome or chromium to launch Chrome or Chromium on Android, or safari to launch Mobile Safari on iOS. Note that this capability is not required for Android if you specify app-packageand app-activity capabilities (see below).

/abs/path/to/my.apk

or

http://myapp.com/app.ipa,chrome, chromium on Android, safari on iOS

browserName (for Selenium compatibility) should always be ”;  this exists because some clients require it to be sent
Device The kind of mobile device or emulator to use iphone, ipad, selendroid, firefoxos, android, mock_ios
Version Android API version, iOS Version

Android —  4.2/4.3

iOS       — 6.0/6.1/7.0

·         Android Only

Capability

Description

Values

app-activity Activity name for the Android activity you want to launch from your package MainActivity, .Settings
app-package Java package of the Android app you want to run com.example.android.myApp,com.android.settings
app-wait-activity Activity name for the Android activity you want to wait for SplashActivity

4

 

Appium’s Pros and Cons:

Pros:

  • The beauty of Appium is that, all the complexities are under the hood of Appium server and for an automation developer the programming language and the whole experience would remain same irrespective of the platform he is automating (iOS or Android).
  • The other benefits of Appium is that it opens the door to cross-platform mobile testing which means the same test would work on multiple platforms.
  • Unlike other tools Appium doesn’t require you to include some extra agents in your app to make it automation friendly. It believes in the philosophy of testing the same app which we are going to submit in the app store.
  • It is developed and supported by Sauce Labs and it is getting picked really fast with in the WebDriver community for mobile automation.
  • It can automate Web, Hybrid and Native mobile applications.

Cons:

  • Scaling up is an important consideration with Continuous Integration and Appium comes across as a great tool to fulfill this expectation. The reason for this is a technical limitation, in iOS we can only run one instance on Instruments per Mac OS so we can only run our iOS scripts on one device per mac machine. So if we want to run our tests on multiple iOS devices at the same time then we would need to arrange the same number of Mac machines, which would be costly affair. But this limitation can be resolved if we execute our scripts in Sauce Lab’s mobile cloud which at present supports running scripts on multiple iOS simulators at the same time.
  • Appium uses UIAutomator for Android automation which only supports Android SDK Platform, API 16 or higher so to support the older APIs they have used another open source library called Selendroid. So I would not say it as a limitation but it is definitely an overhead on the configuration side.

Sending Mail to Gmail Account via SSL

package org.mail;

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class ConfigureAndSendMail {

private Properties properties = System.getProperties();

private String mailTo = “from@gmail.com”; //Recipient’s email ID needs to be mentioned
private String mailFrom = “to@xyz.com”; //Sender’s email ID needs to be mentioned
private String hostName = “smtp.gmail.com”; //Host name of the gmail server
private String port = “465”; //Communication Port for SSL communication
private String subject=”Default Subject”; //Variable for storing the mail subject
private String message=”This is the default message”; //Variable for storing the message body
private String userName=null; //Variable for storing the user name
private String password=null; //Variable for storing the password
private Session session = null; //Variable for storing the session instance

/**
* Setter method for setting mailTo variable
* @param mailTo
* @return this
*/
public ConfigureAndSendMail setMailTo(String mailTo)
{
this.mailTo=mailTo;
return this;
}

/**
* Setter method for setting mailFrom variable
* @param mailFrom
* @return this
*/
public ConfigureAndSendMail setMailFrom(String mailFrom)
{
this.mailFrom=mailFrom;
return this;
}

/**
* Setter method for setting hostName variable
* @param hostName
* @return this
*/
public ConfigureAndSendMail setHost(String hostName)
{
this.hostName=hostName;
return this;
}

/**
* Setter method for setting port variable
* @param port
* @return this
*/
public ConfigureAndSendMail setPort(String port)
{
this.port=port;
return this;
}

/**
* Setter method for setting subject variable
* @param subject
* @return this
*/
public ConfigureAndSendMail setMailSubject(String subject)
{
this.subject=subject;
return this;
}

/**
* Setter method for setting message variable
* @param message
* @return this
*/
public ConfigureAndSendMail enterMessage(String message)
{
this.message=message;
return this;
}

/**
* Setter method for setting user name variable
* @param userName
* @return this
*/
public ConfigureAndSendMail setUserName(String userName)
{
this.userName=userName;
return this;
}

/**
* Setter method for setting password variable
* @param password
* @return this
*/
public ConfigureAndSendMail setPassword(String password)
{
this.password=password;
return this;
}

/**
* Method to set the following mail properties:
* 1.)mail.smtp.host
* 2.)mail.smtp.socketFactory.port
* 3.)mail.smtp.socketFactory.class
* 4.)mail.smtp.auth
* 5.)mail.smtp.port
* All the properties will be set to default if not
* explicitly set by the user using the setter methods
*/
private void setMailProperties()
{
properties.put(“mail.smtp.host”, hostName);
properties.put(“mail.smtp.socketFactory.port”, port);
properties.put(“mail.smtp.socketFactory.class”,
“javax.net.ssl.SSLSocketFactory”);
properties.put(“mail.smtp.auth”, “true”);
properties.put(“mail.smtp.port”, port);
}

/**
* Method to establish a session after authenticating the User
* Requires to atleast set the User Name and Password
* using the setUserName and setPassword method
* @return this
*/
public ConfigureAndSendMail establishSession()
{
final String userName=this.userName;
final String password=this.password;

setMailProperties();
session = Session.getDefaultInstance(properties,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName,password);
}
});

return this;
}

/**
* Method to send the email with all the configured fields
* @return true/false
*/
public boolean sendEmail()
{
try
{
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);

// Set From: header field of the header.
message.setFrom(new InternetAddress(mailFrom));

// Set To: header field of the header.
message.addRecipient(Message.RecipientType.TO,
new InternetAddress(mailTo));

// Set Subject: header field
message.setSubject(this.subject);

// Now set the actual message
message.setText(this.message);

// Send message
Transport.send(message);

System.out.println(“Sent message successfully….”);

return true;
}
catch (MessagingException mex)
{
System.out.println(“Failed to send message….”);
mex.printStackTrace();
return false;
}
}
}

//Main Class invokign the ConfigureAndSendMail Class

package org.mail;

import javax.mail.MessagingException;

public class MainMailClass {

/**
* @param args
* @throws MessagingException
*/
public static void main(String[] args) throws MessagingException {

new ConfigureAndSendMail().setUserName(“account@gmail.com”)
.setPassword(“account-password”)
.setMailTo(“to@xyz.com”)
.establishSession()
.sendEmail();
}
}

Page Object Pattern & Page Factory

Why should the Page Object Model be used

A Page Object represents different pages/sections of a website as objects within the test script. In today’s software development lifecycle, there is a constant need of the software to stay up-to-date to the ever-growing technology. Hence, many clients are opting to work on a scrum-based software development methodology.
With the Page Object Model, even if the UI implementation of the website goes through modifications, test script level changes will only be limited to the Page Objects. This model helps in enhancing the tests, makes them highly customizable, reduces the code duplication, builds a layer of abstraction and finally hides the inner implementation from tests.
In Figure 1 below, a clear flow is seen from the test cases to the webpage. Initially, the test scripts interact with the page objects. These page objects interact with the Selenium WebDriver which contains selenium functions that finally hit the webpage and perform actions on it.

POM

By applying principles of object-oriented development, we can create a class that serves as an interface to a web page in the application & modeling its properties and behavior. This helps in creating a layer of separation between the test code and the page-specific code by hiding the technical implementation such as locators used to identify elements on the page, layout and so on.
The Page Object design pattern provides tests for an interface where a test can operate on that page in a manner similar to the user accessing the page but by hiding its internals. As an example, think of the inbox of any web-based email system. Amongst all the services that it offers, one of them is typically the ability to compose a new email, choose to read a single email and to list the subject lines of the emails in the inbox. How these are implemented shouldn’t matter to the test. The tests should use objects of a page at a high level where any change in layout or attributes used for the fields in the underlying page should not break the test.

Layers of the framework in Page Object Model

 Untitled

 Test Case Layer

This is the first layer from where the flow of the automation framework starts. It usually contains functions which in turn call functions in the Page Object layer (described below). It is important to note that assertions are done at this level.

Page Object Model Layer

At the second layer, i.e. the page object specific later, a level of abstraction is provided by hiding the technical implementation of the test case. The code for this layer is implemented by writing functions for the services provided by a particular page.

Let us take an example of the Gmail portal. An email workflow typically consists of a user logging in, seeing the list of emails, clicking on Compose, writing the email and finally sending it. If we observe this workflow carefully, it is seen that all these actions are performed on different webpages on the same website.

The below steps explain the flow of the test case keeping the page object model in picture:

Step 1. Login page

a. Enter login credentials

b. Click Login button

Step 2. Worklist Page

a. List of emails seen

Step 3. Compose Page

a. Write email

b. Click Send

Step 4. Worklist Page

a. Verify that list of emails are seen

At the test script level for the Page Object Model, all these pages have their own class files. These class files contain the application-specific locators which identify the page objects (like username text box locator, sign-in button locator etc.) Along with locators the locators, page-related functions are also included (like click on the login button, type values in the username box etc.)

Now assume that on performing an action, the control moves to another page. In this case, the function performing that action should return an object of the new page.

For example, when a user clicks on Login button in the Gmail portal, he/she is navigated to the user’s Inbox; here the function implementing the click on the login button should return the object of the navigated page i.e. the User’s Inbox page. This is depicted in the code snippet below:

Untitled

Another point worth noting is that the Page objects are commonly used for testing, but should not make assertions themselves. Their responsibility is to provide access to the state of the underlying page. It’s the responsibility of the Test Case Class to carry out the assertion logic.

Untitled

Page objects are a classic example of encapsulation. They hide the details of the UI structure from the tests. It’s a good design principle to look for situations like this where you can ask yourself “how can I hide some details from the rest of the software?” As with any encapsulation this yields two benefits:

  1. Business logic resides at a single place which can be modified without affecting rest of the components of the system
  2. It provides separation between business logic and the UI details

 Selenium Function Layer

This section contains the Selenium WebDriver code written in the form of wrapped functions which directly interacts with the AUT.

Page Object Model in action

Page Object Model is explained below with an example. Let us consider the simple illustration of logging into a demo website.

This portal consists of a UserName Text Box, Password Text Box and a Login button as shown below:

Untitled

UML diagram of the test case workflow

The UML diagram of the entire flow is shown below starting from the Test Cases (both positive and negative). These test cases interact with the SignIn Class (implemented as Page Object Pattern) consisting of services (offered by the page) in the form of functions. This layer provides abstraction and hides the inner implementation from tests. The Page Object Class in turn communicates with the Selenium functions implemented in another file which provides another layer of abstraction to the test cases. Last but not the least, Selenium functions class interacts with the AUT (application under test), since this is what they are designed to do.

 Untitled

Sign-in Page modelled as Page Object Pattern

The Page Object Model for the Sign-in Page contains the representation of the application’s login page, and the services provided by the page via public methods.

The class shown below consists of object locators along with three elementary functions (enterLoginId (String), enterPassword (String), clickLoginButton ()) and a master function- login (String, String)

SignIn Page Class

 Untitled

As per the UML diagram, the SignInPage (implemented as POM) interacts with the Selenium functions (implemented in the BrowserActions class – defined below) which in-turn interacts with the UI components of the application under test i.e. performing actions like click, entering test data, etc.

The BrowserActions class shown below consists of wrapper functions – ‘text_Set(By, String)’ and ‘element_Click(By)’ which internally make use of  Selenium WebDriver functions like findElement(By), sendKeys(String), click() etc.

BrowserActions Class

 Untitled

Advantages of using Page Objects

1.)  There is a clean separation between test code and page-specific code such as locators and layout

2.)  There is a single repository for the services or operations offered by the page rather than having these services scattered throughout the tests

3.)  In test automation, it is used for enhancing test maintenance and reducing code duplication

4.)  It is an object oriented class which acts as an interface to the actual page of an application

5.)  Tests use methods of the Page Object whenever they need to interact with the UI page. The advantage over here is that if the UI for a page changes, then there is no need to change the test code. Just the code within the Page Object needs to change. Subsequently, all changes which are required to be done due to UI changes are at one place i.e. Page Object.

Page Factory

In order to provide additional support for the Page Object pattern, the package ‘org.openqa.selenium’ has a PageFactory Class. One can import the PageFactory class in Java using the following statement:

import org.openqa.selenium.support.PageFactory;

Usage

  1. Declare fields in a java class (say, UsingPageFactory.java) that are WebElements or List<WebElement>. Also, the names of the WebElement(s) should be the same as the ID/name of the UI element in the HTML DOM.

Example,

  1. In order to initialize the WebElements, we need to use the PageFactory.initElements method as shown below:
  1. Step 1 and Step 2  can be summarized using the below code snippet:

 Untitled

Explanation

The name of the private field i.e. ’firstNameTxtBox’ in the ‘UsingPageFactory’ Java class is assumed to be either the “id” or “name” of the element on the HTML page.

In the example above, the line:

firstNameTxtBox.sendKeys (text);

is equivalent to either

driver.findElement(By.id(“firstNameTxtBox”)).sendKeys(text);

Or

driver.findElement(By.name(“firstNameTxtBox”)).sendKeys(text);

The WebDriver’s instance that’s used in the above statements is the one that’s passed to the PageFactory’s initElements method.

 Note:

If both the statements fail to find the element firstNameTxtBox on the webpage using the findElement method, then, NoSuchElementException is thrown.

 Making Use of Annotations (@FindBy and @CacheLookup)

  • @FindBy

In order to give meaningful name to the fields rather than a name which is either “id” or “name” of the html element or list of elements, it is advisable to use the annotation @FindBy

Untitled

You can either use this annotation by specifying both “how” and “using”

@FindBy(how = How.ID, using = “web.id.one”) WebElement userName;

or by specifying one of the location strategies (e.g. “id”) with an appropriate value to use.

@FindBy(id = “web.id.one”) WebElement userName;

Both the above statements point to the same WebElement i.e. userName and below two annotations point to the same list of elements:

Untitled

Untitled

PageFactory.initElements

The static method initElements is used to initialize each of the WebElements and List<WebElement> instance that have been declared, assuming that the field name is also the HTML element’s “id” or “name”. This means that for the Page class below, the “submit” WebElement is internally located using the XPath expression “//*[@id=’submit’]” or “//*[@name=’submit’]”.

Untitled

On the other hand, if @FindBy annotation is used, then the WebElement is located using the search criteria specified by the constant of enum <How> and its value assigned to using.

In the below example, the location strategy specified is ID and its value is submitButton.unique.id

Untitled

  • @CacheLookup

 One shortcoming is that every time a method is called on the WebElement, the driver will again find it on the current page. This behavior is desired on simple webpages where we know that element is always going to be present when an action is performed. We also know that we won’t be navigating away from the page and returning. In such a case, it would be handy if we could “cache” the element once it is found.

Consider the below code snippet where the @CacheLookup annotation is being used to eliminate the searching of the searchBox WebElement every time an action is performed using it.

Untitled

But because of the @CacheLookup annotation we are more likely to encounter the StaleElementExceptions since this annotation will find the element once and then keep a reference to it. On the contrary, if this annotation is not used and every time there is a reference to a WebElement, it will go and find it again so you shouldn’t see StaleElementExceptions.

Notes

  • If you use PageFactory, you can assume that the fields are initialized. If you don’t use PageFactory, then NullPointerExceptions will be thrown if you make the assumption that the fields are already initialized
  • List<WebElement> fields are decorated if and only if they have @FindBy or @FindBys annotation. Default search strategy “by id or name” that works for WebElement fields is hardly suitable for lists because it is rare to have several elements with the same id or name on a page.
  • WebElements are evaluated lazily. It means if you never use a WebElement field in a PageObject, there will never be a call to “findElement” for it.

 Using PageFactory in PageObject Model

Referring to the same code in the section “Sign-in Page modelled as Page Object Pattern” the PageFactory.initElements has been used to initialize all the private instance variables (i.e the WebElements) of the SignInPage class.

Untitled

All the three private WebElements usernameTextBox, passwordTextBox and loginButton in the below code snippet are automatically located/initialized when the class is instantiated using the PageFactory.initElements (driver, SignInPage.class)

Untitled

Similarly, the private WebElement welcomeMessage in the HomePage class is instantiated using the same method i.e.  PageFactory.initElements(driver, this)

Untitled

Once the WebElements have been initialized, they can be used to perform various actions like sendKeys(String), click(), etc on the AUT(application under test) as shown below in the below code snippet:

 Untitled

Conclusion:

The Page Object is a Design Pattern which has become popular in test automation and is regarded as one of the best practices in Selenium.