Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should elements ever be made available outside of a page object?

This is a question I cannot find a definitive source on and am hoping to get some answers based on users previous experience mainly with explanations as to why a certain approach DID NOT work out.

I am using webdriver for automation via Protractor and am having a debate on whether or not page elements should ever be made available outside of page object itself. After researching it appears that there are a few different approaches people take and I cannot fully grasp the long term implications of each.

I've seen the following different implementations of the page object model:


Locators are declared in the page object and exported

This is my least favorite approach as it means element are actually being identified in the test. This seems like a bad standard to set as it could encourage automaters to use new locators, not from the page object, directly in the application. Also any which require any dynamic information cannot be directly set when PO is initialized require further editing.

pageobject.js

export default class HomePage {
    constructor() {
        this.passwordField = '#password';
        this.usernameField = '#user';
    }
}

test.js

const homePage = new HomePage();
$(homePage.usernameField ).sendKeys('admin');
$(homePage.passwordField ).sendKeys('password');

Elements declared in page object and exported, locators not

pageobject.js

export default class HomePage {
    constructor() {
        this.passwordField = $('#password');
        this.usernameField = $('#user');
    }
}

test.js

const homePage = new HomePage();
homePage.usernameField.sendKeys('admin);
homePage.passwordField.sendKeys('password);

Elements declared in Page Object and only used directly within the page object, only methods exported

This is the approach I have used in the past and we ended up with many, many functions. For instance we had setUsename(), getCurrentUsername(), getUsernameAttibute(), verifyUsernameExists() and same for password element and many other elements. Our page object became huge so I don't feel like this is the best approach any longer. One of the advantage however is that our tests look very clean and are very readable.

pageobject.js

export default class HomePage {
    constructor() {
        var passwordField= $('#password');
        var usernameField = $('#user');
    }

    setUserName(name){
       username.sendKeys(name);
    };

    setPassword(password){
       passwordField.sendKeys(password);
    };
}

test.js

const homePage = new HomePage();
homePage.setUsername('admin');
homePage.setPassword('123');

I'm very interested to get some feedback on this so hopefully you can take the time to read.

like image 258
DublinDev Avatar asked Jul 29 '19 09:07

DublinDev


People also ask

Why should I use page object?

Page Object Model, also known as POM, is a design pattern in Selenium that creates an object repository for storing all web elements. It is useful in reducing code duplication and improves test case maintenance. In Page Object Model, consider each web page of an application as a class file.

What are the advantages of page object model?

Advantages of the Page Object Model: According to the Page Object Model, you should keep the tests and element locators separately. This will keep the code clean and easy to understand and maintain. The Page Object approach makes automation framework in a testing programmer friendly, more durable and comprehensive.

Can we use assert in page object model?

Page objects themselves should never make verifications or assertions. This is part of your test and should always be within the test's code, never in an page object.


2 Answers

I prefer and believe that the last approach is the best.

Keeping aside the fact that we are talking about automation, any good/great software has following traits.

  • It is composed of individual modules/pieces/components
  • Each individual module/piece/component is cohesive in terms of data/information (selector, webdriver API calls in case of automation) specific to its API/methods to interact with the data.

The last approach provides just that with the added benefit of test cleanliness that you pointed out.

However, most of the times, for whatever reasons, we tend to ignore the modularity and make the existing POs bloated. This i have seen and was part of. So, in way, POs getting bloated is not because of the approach but the way automators/testers/developers being conscious to keep POs modular, composed and simpler. This is true whether it is about POs or the application functional code

Boated POs:

Specific to the problem of bloated POs, check if you can separate out the common elements out of the POs. For ex. Header, Footer, Left Nav, Right Nav etc are common across pages. They can be separated out and the POs can be composed of those individual pieces/sections.

Even within the main content, separate common content (if its across two or more pages if not all) into their own component API if its logical and can be reused across pages.

It is normal that the automation specs perform extensive regression for ex. an element is a password field, length of the text box is so and so etc. In such cases it doesn't make sense to add methods for every single spec use case (or expect). Good thing is, here also the commonality takes the centerstage. Basically, provide API that is used across specs and not if its used in one spec, literally.

Take for ex. a password field should be masked. Unlikely that you would want to test that in multiple spec files. Here, either we can add a method for it in LoginPO like isPasswordMasked() or we can let the password field accessible from LoginPO and the spec do the actual check for password type field. By doing this, we are still letting LoginPO in control of password field information that matters to other API (login(), logout() etc) i.e only PO knows how and where to get that password element. With the added advantage of pushing spec testing to spec file.

POs that expect/assert

At any point, its not a good idea to make any testing (or expect) to be part of the PO API. Reason(s):

  • The PO and its API is reusable across suites and should be easy for anyone to look at it and understand. Their primary responsibility is to provide common API.
  • They should be as lean as possible (to prevent bloating up)
  • More importantly, browser automation is slower inherently. If we add test logic to the POs and its API methods, we will only make it slower.

FWIW, i have not come across any web page that mandates a bloated API.

Exposing Elements in POs:

It depends on the use case i believe. May be an element that is used in only one spec can be a base case to expose. That said, in general, the idea is that the specs should be readable for the tester/developer AND for those who look at them later. Whether its done with meaningful element variable name or a method name is largely a preference at best. On the other hand, if an element is meant to have a bit of involved interaction (for ex hover open menu link) its definitely a candidate for exposing through API alone.

Hope that adds some clarification!

like image 61
Rakesh Kumar Cherukuri Avatar answered May 29 '23 18:05

Rakesh Kumar Cherukuri


The last way is the correct way to implement page objects. The idea behind the page object is that it hides the internals of the page and provides a clean API for a script to call to perform actions on the page. Locators and elements should not be exposed. Anything you need to do to the page should be exposed by a public method.

One way you can avoid a getter and setter for each field on the page is to consolidate methods. Think about actions that the user would take on the page. Rather than having .setUsername(), .setPassword(), and .clickLoginButton() methods, you should just have a login() method that takes the username and password as parameters and does all the work to log in for you.

References

Martin Fowler is generally considered the inventor of the "page object" concept but others coined the name "page object". See his description of the page object.

Selenium's documentation on page objects.

like image 21
JeffC Avatar answered May 29 '23 18:05

JeffC