This post aims at explaining a Test Automation design pattern I have com across during my work @ CESAR (Recife) writing software to execute E2E Test Automation. The Page Object Model applied to Protractor.
After conceptualizing different tools available for the job, the team opted for using a framework called Protractor [Github], which is an E2E Test Framework for Angular Apps, according to the project’s page http://www.protractortest.org.
Developed to fit Angular Apps testing, it serves as a wrapper for Selenium Webdriver, providing advantages when compared to Selenium alone using Java. Apart from the element locators specific for Angular Apps, one of the main advantages is automatic waiting: it executes the next step of your tests as soon as the page finishes all pending tasks.
But the main purpose of this post is to provide you guys with a practical idea of designing an easily maintainable modularised auto-test suite based on the Page Object model. The tools used for the design of this suite were basically Node.js and Protractor, which wraps WebdriverJS and uses the Jasmine framework for BDD (Behavior Driven Development).
To design a maintainable, decoupled test automation solution, we should consider the same approach used for Java Page Objects. Each page should be encapsulated into their own object and instantiated at runtime. The directory structure destined to hold the Testware is the following. Notice we have used the same Java class notation, naming objects instantiated at runtime with Upper Case.
|---- protractor
|---- configurationfile.js
|---- package.json
|---- data
|---- set_one.js
|---- set_two.js
|---- dumpfile.sql
|---- pageobjects
|---- LoginPage.js
|---- HomePage.js
|---- connections
|---- ConnectDatabase.js
|---- ConnectAMQP.js
|---- testconditions
|---- Environment_one.js
|---- Environment_two.js
|---- tests
|---- sanity.js
|---- functional.js
|---- feature_uat.js
|---- node_modules
|---- jasmine-reporters
|---- mysql
We are going to use Node.js features to export Javascript objects as functions to the execution environment. For this reason, the design chosen was to define every object as an anonymous function. See below an example of the LoginPage.js.
var LoginPage = function() {
//Constants - could be taken from a test DB for multilevel access test
const URL = "http://localhost/login";
const USERNAME = "roger";
const PASSWORD = "1234";
const LOGIN_OK = "Login Successful";
//Attributes - WebElements of the page
this.usernameElement = element(by.name("field_name_here"));
this.passwordElement = element(by.id("field_id_here"));
this.submitElement = element(by.id("field_id_here"));
this.messageLabel = element(by.id("label_id_here"));
//Methods - Actions performed at the page using the WebElements
this.getLoginPage = function() {
browser.get(URL);
};
this.login = function() {
this.usernameElement.sendKeys(USERNAME).
then(this.passwordElement.sendKeys(PASSWORD).
then(this.submitElement.click()
)
);
};
};
//This will export the module to Node.js runtime environment
module.exports = LoginPage;
At the test suite, you will invoke the Page Objects you will need to run you test, in this example a part of my sanity check, sanity.js.
//A page object required and Instantiated
var LoginPage = require("../pageobjects/LoginPage");
var loginPage = new LoginPage();
//The test suite and specs (it)
describe('Sanity Test Suite', function() {
describe('Login test', function() {
it('Should login and verify success', function() {
loginPage.login().then(function() {
loginPage.messageLabel.getText().then(function(text) {
expect(text).toBe(loginPage.LOGIN_OK);
})
});
}, 10000);
});
});
Every element is a WebElement object, containing methods, such as click(), for example:
pageInstance.pageElement.click();
If you create a method within the page object to print the object “this” to the terminal, you will be able to see all the elements mapped with their methods, for example:
Page Object:
this.printElements = function() {
console.log(this);
};
Test file:
pageObject.printElements();
What you see is something like this (where passForm is the name of an attribute mapped to a WebElement):

All these functions you may use to perform many actions. They all return promises. For a more detailed look over the methods, please check the API.
For a great kick-off on how to use Protractor, checkout this Tutorial.
Best,
Raf.
Like this:
Like Loading...