Web Test Automation – Page Object Model with Springboot, TestNG and Selenium WebDriver

Read the previous blog before continuing with this topic. Click here to read more.

Let’s create a Page Object Model design pattern with Spring Boot, TestNG and Selenium WebDriver. From the previous blog, we have the project created with Spring Boot and TestNG. Its time to extend it with Selenium WebDriver.

Add Selenium WebDriver maven dependency

<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.11.0</version>
</dependency>

Re-build the project.

Update WebDriverBase.java file to initialize the ChromeDriver
Add chrome driver (chromedriver.exe) to src/main/resources.

package base.firstautomation;
 
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
 
@Component
public class WebDriverBase {
 Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
  
 WebDriver webDriver = null;
  
 public void setChromeDriver(){
  logger.info("####### SET UP CHROME DRIVER ######");
  System.setProperty("webdriver.chrome.driver",
    System.getProperty("user.dir") + "\\src\\main\\resources\\chromedriver.exe");
  webDriver = new ChromeDriver();
  webDriver.manage().window().maximize();
 }
  
 public void init() throws InterruptedException {
  logger.info("This is init in Webdriver session");
  String browserType="chrome";
   
  switch(browserType){
  case "chrome":
   setChromeDriver();
   break;
  default:
   System.out.println("#### No Browser Type provided #####");
  }
   
  logger.info("Load the server url: " + "https://www.live.guru99.com");
  webDriver.get("https://www.live.guru99.com");
 }
  
 public void quitDriver(){
  webDriver.quit();
  logger.info("Close browser");
 }
  
 public WebDriver getWebDriver() {
  return webDriver;
 }
}

TestCaseOnePage.java

Create a class file to have the page objects :

If you see the page object class (TestCaseOnePage.java), there is no PageFactory.initElements(). The reason is, the same will be added in a class which implements BeanPostProcessor interface. We will see that soon.

Why PageFactory.initElements()?
PageFactory.initElements(driver, pageObjectClass) implicitly creates the findElement calls behind the scene.

package base.firstautomation;
 
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
 
public class TestCaseOnePage {
 
 @FindBy(xpath = "//a[contains(text(),'Mobile')]")
 public WebElement linkMobile;
 
 public void getNoOfLinks() throws InterruptedException {
  linkMobile.click();
  Thread.sleep(10000);
 }
}

Before adding PageFactory.initElements(driver, pageObjectClass) in the class which implements BeanPostProcessor interface, we have to create a custom annotation. Why? Lets see this

Create custom annotation

package base.firstautomation;
 
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
 
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
 
@Retention(RUNTIME)
@Target(TYPE)
@Component
@Scope("prototype")
@Lazy
public @interface PageObjects {
 String value() default "";
}

Create the class which implements BeanPostProcessor

package base.firstautomation;
 
import org.openqa.selenium.support.PageFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
 
@Component
public class PageObjectBeanPostProcessor implements BeanPostProcessor {
 
 @Lazy
 @Autowired
 private WebDriverBase driver;
 
 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  if (bean.getClass().isAnnotationPresent(PageObjects.class)) {
   System.out.println("This is in Bean for page objects with annotation PageObjectDesktop processror");
   PageFactory.initElements(driver.getWebDriver(), bean);
  }else {
   System.out.println("This is in Bean for page objects without annotation PageObjectDesktop processror");
  }
  return bean;
 }
 
 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  return bean;
 }
}

TestCaseOnePage.java with @PageObjects

Now time to update TestCaseOnePage.java with the custom annotation @PageObjects

package base.firstautomation;
 
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
 
@PageObjects
public class TestCaseOnePage {
 
 @FindBy(xpath = "//a[contains(text(),'Mobile')]")
 public WebElement linkMobile;
 
 public void getNoOfLinks() throws InterruptedException {
  linkMobile.click();
  Thread.sleep(10000);
 }
}

Go back to the FirstTestCase.java

And update it as per below so that it will mimic a real world test case:

package base.firstautomation;
 
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.testng.annotations.Test;
 
public class FirstTestCase extends BaseTest {
 Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
 
 /**
  * By default, Spring creates all singleton beans eagerly at the
  * startup/bootstrapping of the application context. The reason behind this
  * is simple: to avoid and detect all possible errors immediately rather
  * than at runtime. However, there're cases when we need to create a bean,
  * not at the application context startup, but when we request it. So @Lazy
  * is used for the same
  */
 @Lazy
 @Autowired
 TestCaseOnePage testCaseOnePage;
 
 @Test
 public void firstTest() throws InterruptedException {
  logger.info("Test case 2 executed");
  testCaseOnePage.getNoOfLinks();
  logger.info("Test case TWO is completed");
  // No asserttions is used as its an sample test case. You can add it
 }
}

Run the test case:

Now we are done with PageObject, Custom annotation, BeanPostProcessor, and updated Test case, its time to run the test case and see the logs


With this framework:

Test team can focus on the writing test cases rather than focussing on how to read a properties file, how to implement log4j etc.

Hope this topic is useful to you.


Leave a Reply

Your email address will not be published. Required fields are marked *

About This Site

The main aim of this site is to share knowledge with fellow software test specialists who are keen to grow both technically and professionally.

Categories