Saturday, April 11, 2020

TestProject Test Automation tool

What is TestProject?


From testproject.io:
  • Free Test Automation for All.
  • Cloud Hosted, Community Powered.
  • TestProject is a free end-to-end test automation platform for web, mobile, and API testing that’s supported by the #1 test automation community.

Anyway we are not going to discuss advantage and disadvantages of TestProject tool but we will talk about Addons and Coded Test cases which are advanced topics. This will be useful while documenting the test cases. Apart from Addons and Coded Test cases, following will be discussed as well.

More in TestProject page

Monday, March 23, 2020

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

Continuation from previous blog 'Web Test Automation with Springboot and TestNG'.....

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>

Now re-build the project.

Update WebDriverBase.java file to initialize the ChromeDriver

Add chrome driver (chromedriver.exe) to src/main/resources.

Let's update WebDriverBase.java class



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;
 }
}


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 



If you see the framework, you can see we can remove lot of boiler plate codes and few other benefits as I have mentioned previously. 

Please leave a comment if you find the blog useful.

Click here to see how to use the properties file without writing many lines of code. 

Web Test Automation with Springboot and TestNG

In the market there are lot of test automation tutorials which describe the standard framework involving Selenium Webdriver for Java, TestNG & Maven. And the most widely used design pattern is Page Object Model (POM). In this blog, we will see an additional framework 'Spring Boot' which is used to develop production grade application in short amount of time.

I will try to explain the framework from s/w test perspective. The details of Spring Boot is not in the scope of the blog.

Let's start..

Spring Boot is an open source framework which provides Java developers with a platform to start with an auto configurable production-grade Spring application. How will it be useful for the Automation Test Specialists?

In most the standard test automation framework, properties files are used :

  • to store the server urls
  • browser details (if you want to run the test cases using chrome or firefox etc)
  • used to store the xpaths
  • internationalization 
To read the properties file, we have to write certain lines of code involving Java file class. What if there is a way to read the properties file without explicitly using Java file class? Spring Boot enables us to do exactly that so that we test automation specialist can focus on writing more test cases quickly. 

Lets see another example :
Log4j is widely used for logging by developer and test specialists. Usage of this involves in downloading the jar, creating corresponding properties file or xml file. Won't be it good to have the feature by default if you create an automation project? Spring Boot enables this feature by default.

 Step by step guide to create test automation framework with the help of Spring Boot.


Developing Your First Spring Boot Application


The simplest way to a create Spring Boot project is to create it using Spring Initializr.


  • Go to Spring Initializr
  • Select Project -> Maven Project 
  • Language -> Java
  • Spring Boot -> The latest version available (its 2.2.5 now)
  • Enter Project Metadata: Group, Artifact 
  • Click 'Generate - Ctrl + Enter button' to download the project
  • The project will be downloaded with the artifact name. In our example its 'firstautomation'



  • Unzip the file 
  • Import the project as a Maven project to Eclipse (Version: Neon.3 Release (4.6.3) is used for the tutorial)
  • Once the project is imported, Build the project. It will look like the following screenshot 



  • Go to src/main/java and check for the FirstautomationApplication.java file. Just make its available. We will not touch it unless required. 


Create a simple test case with TestNG

Now our Spring Application is ready. It's time to create a test cases with the help of TestNG and run it.


  • Add TestNG dependency in pom.xml file and build the project
        <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.8.5</version>
        </dependency>

  • Once the TestNG dependency is added, create a test case
  • Create a TestNG Test Case say FirstTestCase.java and make sure that FirstTestCase class extends AbstractTestNGSpringContextTests abstract class.
  • Create a test case as below and run the test case.
@Test
public void firstTest() {
Assert.assertTrue(4==(2+2), "4 is not equal to 2+2"); 
}

  • The test case will be executed successfully


Test Case code:
package base.firstautomation;

import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.Assert;
import org.testng.annotations.Test;

public class FirstTestCase extends AbstractTestNGSpringContextTests {
  @Test
  public void firstTest() {
  Assert.assertTrue(4==(2+2), "4 is not equal to 2+2");
  }
}




Test Automation Framework 


Now we have seen how to create a TestNG test case and run it successfully. Let's create a test framework mimicking Page Object Model automation framework.

In standard framework we do the set up for the browser session (create the base), and upon completion of the execution, we teardown the session etc.

Lets create a BaseTest.java file for the above mentioned purpose. And will create a WebDriverSession to initialize the browser etc.

BaseTest.java

package base.firstautomation;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import base.firstautomation.FirstautomationApplication;

@SpringBootTest(classes = FirstautomationApplication.class)
public abstract class BaseTest extends AbstractTestNGSpringContextTests{

 //Place holder for webdriversession
 
 @BeforeMethod
 public void setup() throws InterruptedException {
  logger.info("Before method started");
  //function to intialize the driver
 }

 @AfterMethod
 public void tearDown() {
  logger.info("After method started");
  //Function to close the driver
 }
}


Points to note above:

  • @SpringBootTest(classes = FirstautomationApplication.class) annotation is used
  • BaseTest extends AbstractTestNGSpringContextTests and our test case will extend the BaseTest
  • @BeforeMethod / @AfterMethod self explanatory 


FirstTestCase.java looks like the following:
package base.firstautomation;

import org.testng.Assert;
import org.testng.annotations.Test;

public class FirstTestCase extends BaseTest {
  @Test
  public void firstTest() {
  Assert.assertTrue(4==(2+2), "4 is not equal to 2+2");
  }
}


WebDriverBase.java

A Java class decorated with @Component is found during classpath scanning and registered in the context as a Spring bean. Note that - WebDriverBase is decorated with @Component

package base.firstautomation;

import org.slf4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class WebDriverBase {
 Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
 
 
 public void setChromeDriver(){
  logger.info("######## SET UP CHROME DRIVER ########");
 }
 
 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 #################");
  }
 }
 
 public void quitDriver(){
  logger.info("Close browser");
 }
}


Once WebDriverBase.class is defined, update the BaseTest.java file with init() and quitDriver().

BaseTest.java (updated)

package base.firstautomation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import base.firstautomation.FirstautomationApplication;

@SpringBootTest(classes = FirstautomationApplication.class)
public abstract class BaseTest extends AbstractTestNGSpringContextTests{

 //Place holder for webdriversession
 @Autowired
 WebDriverBase webDriverBase;
 
 @BeforeMethod
 public void setup() throws InterruptedException {
  logger.info("Before method started");
  //function to intialize the driver
  webDriverBase.init();
 }

 @AfterMethod
 public void tearDown() {
  logger.info("After method started");
  //Function to close the driver
  webDriverBase.quitDriver();
 }
}

FirstautomationApplication.java

Finally update the FirstautomationApplication.java so that spring can scan the packages to scan for components

package base.firstautomation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan({"base.firstautomation"})
@SpringBootApplication
public class FirstautomationApplication {

 public static void main(String[] args) {
  SpringApplication.run(FirstautomationApplication.class, args);
 }
}



Run the FirstTestCase test case

After running the test case (FirstTestCase.java) the following can be seen from the logs




This concludes a basic test automation framework (minus Selenium WebDriver). Lets see that in next blogs.

And now discuss the advantage of the framework over popular framework available:


  • Reduced boiler plate code
  • No xml or properties file for log4j. Its available in the springboot framework
  • And there is new ClassName() to create the object. It reduces lines of code if you are working a large project having several PagaObject classes.