Page Object Model is one of the most used test automation framework pattern and in this tutorial I will explain you this test automation design pattern. Before, Ege Aksöz also wrote an article on POM with C#. You can also see the definition of POM and the main advantages. Thus, I do not go into fundamentals again. I just want to show you a real-life basic POM example with JAVA.
Our POM framework generally consists of 3 basic abstraction layer. These are tests, pages, and util. Complicated frameworks comprise more abstraction layers. We start from the basics so we will have three abstraction layers. In this article, I will not write any class in Util layer but we will improve our POM framework in later articles and I will add new utility classes to this layer.
Tests: It comprises of our test classes.
Pages: It comprises of page classes.
Util: It comprises of utility and helper classes.
I will select one of the most popular Turkish e-commerce site n11.com as a test website and we will implement basic failed login scenarios with POM & JAVA & TestNG. Let’s Start!
At the end of the project, our project structure will be like this.
It is always better to start with requirements, features, or in other terms our tests. It is like a top-down approach and this approach helps you not to make too many mistakes during creating your test automation framework. Thus, first I will start to create our LoginTest class. Our login scenarios are as follows:
- InvalidUsernameInvalidPassword()
- Given I am at Home Page
- When I go to Login Page
- And I try to login the website with invalid username and password
- Then I see valid error messages
- EmptyUsernameEmptPassword()
- Given I am at Home Page
- When I go to Login Page
- And I try to login the website with empty username and password
- Then I see valid error messages
In test classes:
- We need to instantiate the required Page classes
- Use the appropriate page methods
- Do the assertions
Here is the loginTests.java class:
package tests; import org.testng.annotations.Test; import pages.HomePage; import pages.LoginPage; public class LoginTests extends BaseTest { @Test (priority = 0) public void invalidLoginTest_InvalidUserNameInvalidPassword () { //*************PAGE INSTANTIATIONS************* HomePage homePage = new HomePage(driver); //*************PAGE METHODS******************** homePage.goToN11() .goToLoginPage() .loginToN11("onur@", "11223344") .verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı")) .verifyLoginPassword(("E-posta adresiniz veya şifreniz hatalı")); } @Test (priority = 1) public void invalidLoginTest_EmptyUserEmptyPassword () { //*************PAGE INSTANTIATIONS************* HomePage homePage = new HomePage(driver); //*************PAGE METHODS******************** homePage.goToN11() .goToLoginPage() .loginToN11("","") .verifyLoginUserName("Lütfen e-posta adresinizi girin.") .verifyLoginPassword("Bu alanın doldurulması zorunludur."); } }
We have a lot of reds line in this class, we need to start to resolve them until our project will not have any red lines and problems. Let’s start with the BaseTest class.
BaseTest class contains all common functionalities and variables of test classes and all test classes extend this BaseTest class. This is one of the main features of Object Oriented Design (OOD) “Inheritance“. The code of BaseTest is shown below.
package tests; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; public class BaseTest { public WebDriver driver; @BeforeClass public void setup () { //Create a Chrome driver. All test classes use this. driver = new ChromeDriver(); //Maximize Window driver.manage().window().maximize(); } @AfterClass public void teardown () { driver.quit(); } }
In this class, we declared the driver variable. This is used by all test classes. Also, we wrote @BeforeClass setup method and @AfterClass teardown method. Again, all test classes use these methods.
In the setup method, we create a ChromeDriver and maximize the browser.
In the teardown method, I closed all the browsers with driver.quit(); code line.
That’s all for the base test class.
Now, let’s write the BasePage class. BasePage class contains the common methods of all page classes such as click, writeText, readText, assertEquals etc. Here is it’s code.
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; public class BasePage { public WebDriver driver; public WebDriverWait wait; //Constructor public BasePage (WebDriver driver){ this.driver = driver; wait = new WebDriverWait(driver,15); } //Wait Wrapper Method public void waitVisibility(By elementBy) { wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(elementBy)); } //Click Method public void click (By elementBy) { waitVisibility(elementBy); driver.findElement(elementBy).click(); } //Write Text public void writeText (By elementBy, String text) { waitVisibility(elementBy); driver.findElement(elementBy).sendKeys(text); } //Read Text public String readText (By elementBy) { waitVisibility(elementBy); return driver.findElement(elementBy).getText(); } //Assert public void assertEquals (By elementBy, String expectedText) { waitVisibility(elementBy); Assert.assertEquals(readText(elementBy), expectedText); } }
Now, we can create our page classes. The first one is HomePage.java class. In this class we will declare:
- Constructor
- Page Variables
- Web Elements
- Page Methods
We will have two methods, one of them opens the homepage and the other one opens the login page. Here is the code.
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.WebDriverWait; import sun.rmi.runtime.Log; public class HomePage extends BasePage { //*********Constructor********* public HomePage (WebDriver driver) { super(driver); } //*********Page Variables********* String baseURL = "http://www.n11.com/"; //*********Web Elements********* By signInButtonBy = By.className("btnSignIn"); //*********Page Methods********* //Go to Homepage public HomePage goToN11 (){ driver.get(baseURL); return this; } //Go to LoginPage public LoginPage goToLoginPage (){ click(signInButtonBy); return new LoginPage(driver); } }
Another page class in LoginPage.java class. We will have three methods. One of them does the login operation, the other ones are assertion methods. Checks the login messages as expected or not and set the test fail or pass. Here is the code of LoginPage.java class.
package pages; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; public class LoginPage extends BasePage{ //*********Constructor********* public LoginPage(WebDriver driver) { super(driver); } //*********Web Elements********* By usernameBy = By.id("email"); By passwordBy = By.id("password"); By loginButtonBy = By.id("loginButton"); By errorMessageUsernameBy = By.xpath("//*[@id=\"loginForm\"]/div[1]/div/div"); By errorMessagePasswordBy = By.xpath("//*[@id=\"loginForm\"]/div[2]/div/div "); //*********Page Methods********* public LoginPage loginToN11 (String username, String password){ //Enter Username(Email) writeText(usernameBy,username); //Enter Password writeText(passwordBy, password); //Click Login Button click(loginButtonBy); return this; } //Verify Username Condition public LoginPage verifyLoginUserName (String expectedText) { assertEquals(errorMessageUsernameBy, expectedText); return this; } //Verify Password Condition public LoginPage verifyLoginPassword (String expectedText) { assertEquals(errorMessagePasswordBy, expectedText); return this; } }
Here are the pom.xml and TestNG.xml files.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>n11project</groupId> <artifactId>n11project</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.14.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.14.3</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19</version> <configuration> <suiteXmlFiles> <suiteXmlFile>TestNG.xml</suiteXmlFile> </suiteXmlFiles> </configuration> </plugin> </plugins> </build> </project>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Suite" > <test name="LoginTest"> <classes> <class name="tests.LoginTests"/> </classes> </test> </suite>
When we run our project, we will get below test results ;)
We can improve this POM framework with Utility classes such as DesiredcapabilitiesManager, AJAXWaiter, Assertions, Listeners, Reporters, PropertyReaders, and so on. We will see the improved version of this in the next articles. You can reach the source code of the project on swtestacademy POM JAVA Example Github Page.
Selenium Webdriver Tutorial Series
[fusion_widget_area name=”avada-custom-sidebar-seleniumwidget” title_size=”” title_color=”” background_color=”” padding_top=”” padding_right=”” padding_bottom=”” padding_left=”” hide_on_mobile=”small-visibility,medium-visibility,large-visibility” class=”” id=””][/fusion_widget_area]
See you!
-Onur

Onur Baskirt is a Software Engineering Leader with international experience in world-class companies. Now, he is a Software Engineering Lead at Emirates Airlines in Dubai.
Great article! In my opinion without technical background, POM is one of the hardest part to understand. Well written. Thanks!
I’m waiting for the new articles about it
Thank you, Ozan. I will try to add new articles on an improved version of POM soon.
Really good article.
Thank you @Sargam.
Hi,
I have question about PAGE INSTANTIATIONS,
Is it possible to create a page Instantiations method and call from each @Test to reach the variables and methods ?
Thanks
Yes of course, you can check one of my implementation here: https://github.com/swtestacademy/JSWaiter/tree/master/src/test/java/pages
Hi Onur,
I have a question about the parameterized constructor here. In each page class you are creating a page constructor where you are passing the Webdriver driver and Webdriver wait and then calling a super method with driver and wait variables. So, on instantiating in the Login.test class, the control will flow to base test class first or base page class? could you please explain with a pictorial representation.
Thanks
Hi Shrikant,
Yes, you are right. In BaseTest class I created driver and wait and pass these variables to page classes in specific test classes. We started to send these variables from BaseTest class to Page classes. You understood well.
Hi,
I think If we return ‘this’ or ‘new’ instead of ‘void’, we can use method chaining and write more fluent test cases like
homePage.goToN11().goToLoginPage().loginPage.loginToN11(“”,””);
Of course! You can chain the methods as you wrote ;)
Kaan, I changed the implementation. ;)
Hi Onur,
I opened the project just as you said,
but when when i try to run the test intellij says:
java.lang.Exception: Method setup() should be static
java.lang.Exception: Method teardown() should be static
I understand i can change those methods and driver in basetest to static, but i’m trying to understand why in your code it’s not static,
Thanks
I think you used/imported JUnit instead of TestNG. JUnit wants static setup() and teardown() methods.
you are right, i am using junit instead of testng,
so i changed the setup and teardown to be static and also the the driver in basetest to be static so the setup and teardown can use it
Great! Have a good testing and test automation Eran.
Thank you,
by the way is there a reason why you chose to set webdriver and wait as public and not protected?
Thank you!
They can be protected. :)
Hi Onur,
Thank you for explaining POM in simple way. Have you written article on Utility Class? I tried it but didn’t found. Request you to please share the link with me if you have already created and it is available.
Thank you Sanjay, you can check my Excel article. https:/data-driven-excel-selenium/ In this example, you can find excel utility class. In this way, you can create your own utility classes. ;)
Thank You Onur.
This is by far the best explanation of POM automation framework. The examples are clear, easy to follow and practical. Amazing how you simplied something so abstract for anyone to follow.
Thank you
You are welcome Chris. Thank you for your feedback.
Hi Onur,
Best explanation on POM. can you just guide me where can we create testNG.xml in the heirachy?
Hi Giri,
For TestNG.xml file you can go to reference link and take the sample xml file as a reference. If you have further questions about it, please do not hesitate to ask. ;)
Link: http://testng.org/doc/documentation-main.html
I feel one redundancy here
//*************PAGE INSTANTIATIONS*************
HomePage homePage = new HomePage(driver,wait);
LoginPage loginPage = new LoginPage(driver,wait);
we are doing the page initiation in each test case; can’t we do some better work around so that it should not be declared again and again for each test case.
You can use generics also here is the example: https:/page-factory-selenium-webdriver/ There are some techniques available in this example. One of the other solutions is to create page generator class. Here is the example code is here. Declaration: https://github.com/swtestacademy/JSWaiter/blob/master/src/test/java/pages/Pages.java and usage: https://github.com/swtestacademy/JSWaiter/blob/master/src/test/java/tests/KariyerRecruiterLoginTest.java
how to use PageFactory.initElements if the homepage constructor requires webdriver,, WebDriverWait and Actions as argument like beow:
public HomePage(WebDriver driver, WebDriverWait wait, Actions actions) {
super(driver, wait, actions);
}
Super ofcourse refers to BasePage class
If you want to use PageFactory, it is better to only use driver for the constructor. You are right.
I updated the code. But honestly, I do not like to use PageFactory in my frameworks or I use it mixe-mode. Bys and PageFactory together.
Best Article..
Thank you so much for your kind comment. :)
The site (https://www.n11.com/) you are using for the test is not in English. I would like to try out the test but when I connect to this site, I’m completely lost. Diffcult to get the fields and web elements you are using. Could you plse let me know how I can follow up?
You can use the same concept on any website. Try to write a sample project for amazon.com. Thank you.
Thanks for your reply. What I actually did was to use Chrome with the Translation activated for the site. So I was able to see fields corresponding to web element to act on. For example, for the site in English, I was able to make the following correspondences
Koum Belirie (Location)
Giris Yap (Login)
Uye Ol (Sign Up)
Thank you for the useful tutorial.
Welcome. Thank you for your comment.
I think its better to put asserts in the page like you did ..
and most of the people think that it should be in the test
what is your opinion about that ?
It is better to have sometimes a specific Assert Class to wrap all asserts and then use them in the tests properly. I am working like that in big size automation projects.
Thank you, sir, it helped a lot.
Hi, Onur.
I find that when switching between test cases in the extent report, it would not jump back to the top automatically. May I ask if there is any way to implement it?
Thanks,
Ben