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.