OdeToCode IC Logo

PageObjects and Protractor

Tuesday, January 6, 2015

I’m a fan of the PageObject pattern when writing Protractor tests, because PageObjects make the test code more expressive and readable. When I sit down to write some Protractor tests, I usually write the test code I want to see first, and then try to make it happen.

For example, when writing some tests for a “login and redirect” scenario, I started off with some test specs like the following.

describe("The security application", function () { 

    var secret = new SecretPage();
    var login = new LoginPage(); 

    it("should redirect to login page if trying to view a secret as anonymous user", function () {

    it("should go back to the secret after a login", function () {
        login.login("sallen", "sallen");


These tests require two page objects, the SecretRecipePage and the LoginPage. SInce Protractor tests run in Node, each page can live in a distinct module. First, the SecretRecipePage.

var config = require("./config"); 

var SecretPage = function () { 

    this.go = function() {


SecretPage.url = config.baseUrl + "security/shell.html#/secret"; 

module.exports = SecretPage;

And the LoginPage.

var config = require("./config"); 

var LoginPage = function () { 

    this.go = function() {

    this.login = function(username, password) {
        $(".container [name=username]").sendKeys(username);
        $(".container [name=password]").sendKeys(password);
        $(".container [name=loginForm]").submit();

LoginPage.url = config.baseUrl + "security/shell.html#/login"; 

module.exports = LoginPage;

Both of these modules depend on a config module to remove hard coded URLs and magic strings. A simple config might look like the following.

var config = {
    baseUrl: http://localhost:9000/Apps/

module.exports = config;

Now all the spec file needs to do is require the two page modules …...

var SecretPage = require("../pages/SecretPage");
var LoginPage = require("../pages/LoginPage.js"); 

describe("The security application", function () { 

    // …... 


And presto! Readable Protractor tests are passing.


Gravatar Pras Tuesday, January 6, 2015
Very Nice post. Clear and to the point. I request you to please do a pluralsight course on testing with Angular.
Gravatar Quinntyne Tuesday, January 6, 2015
I support the request for the Pluralsight course!
Gravatar jdn Tuesday, January 6, 2015
Idiotic nitpick question: why do you call it SecretPage vs. SecretRecipePage? Just because or is there a reason?
Gravatar scott Tuesday, January 6, 2015
@jdn: Ah, an unfortunate translation of the real code. Was trying to stick with just "SecretPage" for the post, since it really represents any page that requires credentials to unlock. Thanks for pointing that out.
Ian B Wednesday, January 7, 2015
Very nice post indeed! Really helpful! Another nitpick question from me too, should that last code snippet contain var SecretPage = require("../pages/SecretPage.js"); rather than var SecretPage = require("../pages/SecretPage"); The SecretPage missing the .js?
Gravatar Scott Wednesday, January 7, 2015
@Ian - the .js suffix is optional when you use require in Node. I try to leave it out.
Gravatar Leblanc Meneses Thursday, January 8, 2015
https://github.com/leblancmeneses/RobustHaven.IntegrationTests Most important piece is step "CI setup ResultDiff" This aligns business and development teams. Everyone is speaking the same language and changes to either side is reflected on the output of ResultDiff.
Gravatar Martin Thursday, January 8, 2015
Here's the real world question: How much time do you 'waste' writing tests (in % time spent) And which clients / companies are willing to pay the overhead?
Gravatar Scott Thursday, January 8, 2015
@Martin: Good topic for a future post, thanks! :)
Gravatar Matt Honeycutt Thursday, January 8, 2015
I really like this approach, and you did a great job of explaining it, Scott. Nice job!
Gravatar Byron Thursday, February 12, 2015
Great post. Simplicity , Clarity and Density of expression. Really enjoy your courses Scott. :0
Comments are closed.