Table of Content¶
Introduction¶
Page Objects are a widely used design pattern for test automation frameworks. It allows the abstraction of Squish APIs behind frames/windows that specify the user interaction in more intelligent and meaningful ways. With Page Objects, it is simple to encapsulate test script code and make future maintenance of test scripts easy and much less painful. Designing your test automation solution based on Page Object patterns requires the creation of two layers:
Business layer
Technical layer
Splitting your test automation framework into layers has many advantages. One of them is a better allocation of team members working on test automation. Those with technical and programming knowledge develop and maintain the test automation framework. Using this custom framework, your QA Specialists, having a vast knowledge of the application functionality, but without programming skills will be able to create robust, maintainable and efficient automated test cases easily. Other advantages include reduced maintenance effort, more straightforward troubleshooting and faster adaptation for ever-changing applications under test.
Business layer¶
This layer contains test script code at a very abstract level, easy to create and read even by non-technical team members. Test cases are very short and self-explanatory, focused on functionality tested, not on how the application under test is built. While applications evolve, test script remains unchanged (unless application functionality changes).
Technical layer¶
This layer contains interaction with the Application Under Test using the Squish API. It takes full advantage of Object Oriented Programming, enabling modular design and a reusable test automation framework. In general, a Page Object is a class representing a part of an application (usually a single page, frame, sub-frame, window or dialogue). This class provides an interface to interact with the application at very abstract level. Page Object frameworks must be designed to fulfill the following rules:
Do not expose the internals of the frame
Methods represent the actions the frame offers
Methods may return other Page Objects
Do not make assertions inside Page Objects
Example¶
Let's start by defining what services the AddressBook frame offers a user. In this frame we can create a new entry, save or edit an address book. Additionally the user can view all entries in the address book. In the example below the AddressBook class is created offering the following methods: new (creating new address book), add (adding entry to existing address book), entries (returning lists of all entries in address book), rowcount (returning number of entries in address book).
import names
import squish
import test
from PageObject import PageObject
from AddressbookAdd import AddressBookAdd
class AddressBook(PageObject):
FRAME = names.address_Book_AddressBook
TABLE = names.address_Book_JTable
MENU = names.address_Book_JMenuBar
FILE_MENU = names.file_JMenu
EDIT_MENU = names.edit_JMenu
def __init__(self):
test.log("Opening Address Book")
squish.waitForObject(self.FRAME)
def new(self):
test.log("New Address Book")
squish.activateItem(squish.waitForObjectItem(self.MENU, "File"))
squish.activateItem(squish.waitForObjectItem(self.FILE_MENU, "New..."))
def add(self):
test.log("Add entry to Address Book")
squish.activateItem(squish.waitForObjectItem(self.MENU, "Edit"))
squish.activateItem(squish.waitForObjectItem(self.EDIT_MENU, "Add..."))
return AddressBookAdd()
def entries(self):
''' Returns list of lists with Address entries '''
jtable = squish.waitForObject(self.TABLE)
ee = []
for row in range(jtable.getRowCount()):
rr = []
for col in range(jtable.getColumnCount()):
rr.append(str(jtable.getValueAt(row, col)))
ee.append(rr)
return ee
def rowscount(self):
''' Returns the number of rows in Address Table'''
return squish.waitForObject(self.TABLE).getRowCount()
In the AddressBook class method add returns a new Page Object, because after pressing the menu option "Add", the new dialog window displays as "AddressBook - Add". Therefore this window is represented by a separate Page Object called AddressBookAdd.
import names
import squish
import test
from PageObject import PageObject
class AddressBookAdd(PageObject):
DIALOG_ADD = names.address_Book_Add_Dialog
TEXTFIELD_FORNAME = names.address_Book_Add_Forename_JTextField
TEXTFIELD_SURNAME = names.address_Book_Add_Surname_JTextField
TEXTFIELD_EMAIL = names.address_Book_Add_Email_JTextField
TEXTFIELD_PHONE = names.address_Book_Add_Phone_JTextField
BUTTON_OK = names.address_Book_Add_OK_JButton
BUTTON_CANCEL = names.address_Book_Add_Cancel_JButton
def __init__(self):
test.log("Opening Address Book - Add window")
squish.waitForObject(self.DIALOG_ADD)
def ok(self):
test.log("Press OK button to add new entry to address book")
squish.clickButton(squish.waitForObject(self.BUTTON_OK))
def cancel(self):
test.log("Press Cancel button")
squish.clickButton(squish.waitForObject(self.BUTTON_CANCEL))
In above examples, we use class constructor (init method) as a place for extra synchronization and logging. Any extra code that needs to be executed when a new frame is opened can be put there.
The last piece of the framework is the PageObject class, which contains generic methods, which can be used for every frame. Every PageObject defined in test framework inherits from the PageObject class. In this example, we defined method fill to enter text into any text field in the frame.
import squish
class PageObject:
def fill(self, **kwargs):
for key, value in kwargs.iteritems():
squish.type(squish.waitForObject(getattr(self, key)), value)
Finally, we need to prepare the test case that will check a basic scenario in the address book application.
from PageObjects.Addressbook import AddressBook
def main():
startApplication("AddressBookSwing.jar")
addressBook = AddressBook()
test.compare(addressBook.rowscount(), 0, "AddressBook.rowscount?")
addressBook.new()
addressBookAdd = addressBook.add()
addressBookAdd.fill(TEXTFIELD_FORNAME="Tom", TEXTFIELD_SURNAME="Paw", TEXTFIELD_EMAIL="tp@m.com", TEXTFIELD_PHONE="1234")
addressBookAdd.ok()
test.compare(addressBook.rowscount(), 1, "AddressBook.rowscount == 1?")
test.xcompare(addressBook.entries()[0], [], "AddressBook.entries != 0?")
After executing the test case in the Squish IDE, we can check the Test Results view. All test steps are generated there by the Page Object framework, and therefore are consistent with all test cases for the given application. Moreover, each log represents functional steps in the test cases, which makes an analysis of potential defects in the application more convenient.
Squish IDE: Test Results View