Selenium PhantomJS Tutorial for Headless Automation

In This Article, Selenium Automation With PhantomJS is Explained With Code Examples:

PhantomJS is a headless browser that is primarily used for GUI less automation.

The performance and execution happening on this browser is faster and is generally used in the scenarios where manual monitoring is not required and on those applications which are completely automatable.

PhantomJS is highly recommended in case of overnight script execution, where human monitoring is not required as the execution is quick fast. It also provides options for an automatic screenshot for manual tracking of the script execution process.

selenium automation with phantomjs

PhantomJS Utilization On Webpage Automation

In this article, we will be using the Selenium automation tool to perform functional automation on the PhantomJS browser.

PhantomJS actually instantiates a browser that doesn’t have a GUI interface, but it has all the standard of a browser with a GUI interface like (Firefox, IE, etc.), standard DOM scripting, Ajax calls, etc.

Objective Of Using PhantomJS With Selenium

It is very important to understand the objective of using PhantomJS with Selenium.

We all know that Selenium is a functional automation tool that is used for automating various functionalities of web applications.

Now the objective of PhantomJS is slightly different as it’s a GUI less browser and its primary usage is to automate the test cases with falls under the category of smoke test/validation tests and not full-fledged regressive test automation.

If we automate using Selenium and PhantomJS, we must be careful of choosing the test cases. Another important part is tracking of the execution status of test cases as we cannot physically see the execution.

PhantomJS And Selenium For Web Automation (Basic)

Just like any other browsers with GUI interface (Firefox, IE, Chrome, etc.), for PhantomJS also, Selenium has a standard API to support the automation.

Let’s illustrate the same with a simple code:


import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;

public class PhantomJSTest {

            public void phantomJS() throws InterruptedException, IOException
           {
                     DesiredCapabilities caps = new DesiredCapabilities();
                     caps.setJavascriptEnabled(true);
                     caps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, "D:\\chromedriver\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe");
                     caps.setCapability("takesScreenshot", true);
                     PhantomJSDriver driver = new PhantomJSDriver(caps);
                     String baseUrl = "http://www.google.com";
                     driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
                     driver.get(baseUrl + "/");

                     File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
                     FileUtils.copyFile(scrFile, new File("d:\\PhantomJSSample\\screenshotAfterLaunchingGoogle.jpeg"),true);

                     driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

                     driver.navigate().to("https://selenium.dev//");//Launch URL

                     File scrFile1 = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
                     FileUtils.copyFile(scrFile1, new File("d:\\PhantomJSSample\\screenshotAfterLaunchingURL.jpeg"),true);
                     driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

                    driver.findElement(By.linkText("Download")).click();//Click on the Link

                   driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
                   File scrFile2 = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
                   FileUtils.copyFile(scrFile2, new File("d:\\PhantomJSSample\\screenshotAfterClickingDownload.jpeg"),true);

                   Thread.sleep(2000);
                   int header_size =driver.findElements(By.xpath("(//div[@id='mainContent']//h3[1])")).size();//Get the total count of h3 headers in the page
for(int i=1; i?=header_size; i++)
                {
                      String suggestion_name_xp = "("+"//div[@id='mainContent']//h3[1]"+")"+"["+i+"]";
                      String header =driver.findElement(By.xpath(suggestion_name_xp)).getText();
                      System.out.println(header); //Print the name of headers
                }

                     Thread.sleep(2000);
               }

         public static void main(String[] args) throws InterruptedException, IOException
        {
                     PhantomJSTest pj =new PhantomJSTest();
                     pj.phantomJS();
         }

}

The above code snippet launches Selenium official website on the PhantomJS browser and performs click operation on the download tab. Then it calculates the number of h3 tagged headers of the main content in the download page and prints it.

After the execution of each operation, it takes a screenshot for manual tracking.

Now we will integrate the same test functionality within a framework with log tracking along with the screenshot. Let’s also add automatic mailing along with extent report integration to give it a complete automation wrap, so that we can track the execution result later.

PhantomJS And Selenium For Web Automation (Advanced)

Image of the framework structure

Image of the PhantomJS framework structure

The framework is as the image suggests and is made up of:

  • The Reusable Components which can be reused by each and every test scripts
  • The Test Component which will be created new with each new test case.
  • The Resource Components which are the inputs of the framework like (Web Element locators, URL, etc.)

Here the project is built on Maven along with test framework TestNG. Also, we have used Extent Report. But I am not getting into the details of a Maven project or extent report but just keeping the focus on PhantomJS.

Code details for each of the components are given below. This framework is intended to focus on the implementation of phantomJS, so the framework is designed based on that, but one can definitely extend this framework according to one’s own business specification.

First, we will see what dependencies we need to declare in POM.xml in order to run this project

<projectxmlns=<em>"http://maven.apache.org/POM/4.0.0"</em>xmlns:xsi=<em>"http://www.w3.org/2001/XMLSchema-instance"</em>xsi:schemaLocation=<em>"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</em>>
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.phantom.com</groupId>
        <artifactId>com.phantomjs.com</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <dependencies>
        <dependency>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <version>3.0.2</version>
        <type>maven-plugin</type>
        </dependency>
        <dependency>
                  <groupId>org.seleniumhq.selenium</groupId>
                  <artifactId>selenium-java</artifactId>
                  <version>3.11.0</version>
        </dependency>
        <dependency>
                  <groupId>org.testng</groupId>
                  <artifactId>testng</artifactId>
                  <version>6.8</version>
                  <scope>test</scope>
</dependency>
<!-- 
https://mvnrepository.com/artifact/com.github.detro.ghostdriver/phantomjsdriver -->
<dependency>
       <groupId>com.github.detro.ghostdriver</groupId>
       <artifactId>phantomjsdriver</artifactId>
       <version>1.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
       <groupId>javax.mail</groupId>
       <artifactId>mail</artifactId>
       <version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.relevantcodes/extentreports -->
<dependency>
       <groupId>com.relevantcodes</groupId>
       <artifactId>extentreports</artifactId>
       <version>2.40.2</version>
</dependency>
</dependencies>
    </project>

POM.xml

Reusable Components


package ReusableElements;

import java.io.File;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.BeforeClass;

import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;

import ScreenShotLoc.ScreenShotLocations;

public class InitiateBrowser
            {
             public static WebDriver driver = null;
             public ExtentReports extent;
             public ExtentTest logger;
             String workingDir = ScreenShotLocations.extentReportLoc;
             PropertyReader pr = new PropertyReader();
             @BeforeClass
             public void InitBrowser()
            {
extent = new ExtentReports(workingDir+"\\ExtentReports\\PhantomJSExectionResults.html", true);
logger=extent.startTest("PhantomJS Implementation");
             String BrowserName = "PhantomJS";
if(BrowserName.equalsIgnoreCase("PhantomJS"))
             {
             DesiredCapabilities caps = new DesiredCapabilities();
                           caps.setJavascriptEnabled(true);
             
             caps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, ScreenShotLocations.PhantomJSdriverLoc);
                         caps.setCapability("takesScreenshot", true);
                         driver = new PhantomJSDriver(caps);
                         List<String> baseUrls = pr.URLReader();
                         String baseUrl= baseUrls.get(0);
                         String altUrl= baseUrls.get(1);
                         driver.get(baseUrl);
                         logger.log(LogStatus.PASS, "Browser Initiated");
                         driver.navigate().to(altUrl);
                         logger.log(LogStatus.PASS, "Navigated to target browser");
                        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

            }
else if(BrowserName.equalsIgnoreCase("Chrome"))
          {
System.setProperty("webdriver.chrome.driver",ScreenShotLocations.ChromedriverLoc);
                         driver = new ChromeDriver();
                         List<String> baseUrls = pr.URLReader();
                         String baseUrl= baseUrls.get(0);
                         driver.get(baseUrl);
                         logger.log(LogStatus.PASS, "Browser Initiated");
                        driver.manage().window().maximize();
                        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
       }
         }
}

InitiateBrowser.java

This piece of code is associated with Browser Initiation.

Here the browser name is hardcoded. But it can be externalized (in properties/excel sheet). One can choose which browser to use, and here we have used PhantomJS.


package ReusableElements;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class PropertyReader
{
          List<String>vals = new ArrayList<String>();
          public List<String> PropReader(){
          Properties prop = new Properties();
          try {
          prop.load(PropertyReader.class.getClassLoader().getResourceAsStream("ObjectRepository.properties"));
vals.add(prop.getProperty("Download_Tab"));
vals.add(prop.getProperty("H3_Headerlist"));
          }
          catch (IOException ex) {
          ex.printStackTrace();
          }
          return vals;
          }
          public List<String> URLReader()
         {
                      Properties prp = new Properties();
                      try {
           prp.load(PropertyReader.class.getClassLoader().getResourceAsStream("APPURL.properties"));
                      vals.add(prp.getProperty("APPURL"));
                      vals.add(prp.getProperty("ALTERNATE_APPURL"));
                      }catch (IOException ex) {
                      ex.printStackTrace();
                      }
                      return vals;
                }
}

PropertyReader.java

This piece of code is associated with the reading properties file which we have used as a web element locator and URL container.


package ReusableElements;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;

public class ReuseableMethods extends InitiateBrowser
{
             public void LinktextClick(String loc)
        {
                         driver.findElement(By.linkText(loc)).click();//Click on the Link
         }
public String GetText(String loc)
        {
             String text= driver.findElement(By.xpath(loc)).getText();
return text;
         }
public void takeScreenShot(String loc) throws IOException
        {
              File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
              FileUtils.copyFile(scrFile, new File(loc),true);
         }
}

ReuseableMethods.java

This piece of code deals with various Selenium functions which we use regularly in our scripts, but we have separated these functions from test scripts to reduce the lines of code in the framework and increase its usability.


package ReusableElements;

import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

public class SendMail {
public void SendAutomatedMail() {

final String from="XXXX";//change accordingly
final String user="XXXX";//change accordingly
final String password="XXXX";//change accordingly
final String to="XXXX";//change accordingly

//Creating the object for the session
      Properties props = new Properties();

props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host","smtp.gmail.com");
props.put("mail.smtp.port", "587");
      Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user,password);
         }
     });

try {
              Message message = new MimeMessage(session);

message.setFrom(new InternetAddress(from));

message.setRecipients(Message.RecipientType.TO,
                      InternetAddress.parse(to));

message.setSubject("TestExecution completed!! Please find the report");

                BodyPart messageBodyPart = new MimeBodyPart();

messageBodyPart.setText("Hi All");
messageBodyPart.setText("please find the attachment");

                Multipart multipart = new MimeMultipart();

multipart.addBodyPart(messageBodyPart);

messageBodyPart = new MimeBodyPart();
                String filename = 
"D:/PhantomJSSample/ExtentReports/PhantomJSExectionResults.html";
                DataSource source = new FileDataSource(filename);
messageBodyPart.setDataHandler(new DataHandler(source));
messageBodyPart.setFileName(filename);
multipart.addBodyPart(messageBodyPart);

message.setContent(multipart);

                Transport.send(message);

           System.out.println("message sent successfully...");

            } catch (MessagingException e) {e.printStackTrace();}
  }
}

SendMail.java

This piece of code deals with sending an automated mail after the execution of the Test Case.

Test Components


package com.phantomjs.com;

import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.testng.annotations.Test;

import com.relevantcodes.extentreports.LogStatus;

import ReusableElements.InitiateBrowser;
import ReusableElements.PropertyReader;
import ReusableElements.ReuseableMethods;
import ReusableElements.SendMail;
import ScreenShotLoc.ScreenShotLocations;

public class TestScripts extends InitiateBrowser
{

@Test
public void TestScript() throws IOException, InterruptedException
{
             ReuseableMethods rm =new ReuseableMethods();
             PropertyReader prop =new PropertyReader();
             SendMail sm = new SendMail();
             String download,h3_header;

             rm.takeScreenShot(ScreenShotLocations.screenshotAfterLaunchingURL);

             driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
             List<String> propvals = prop.PropReader();

                         download= propvals.get(0);
                         h3_header= propvals.get(1);


              driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

              try{
                          rm.LinktextClick(download);//Click on the Link
                          logger.log(LogStatus.PASS, "Validate if download Tab is clickable");
                    }
               catch(NoSuchElementException e)
                  {
                          logger.log(LogStatus.FAIL, "Error while clicking on download Tab");

                   }

               driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

               rm.takeScreenShot(ScreenShotLocations.screenshotAfterClickingDownload);

               Thread.sleep(2000);
               try{
               int header_size =driver.findElements(By.xpath(h3_header)).size();//Get the total count of h3 headers in the page
     List <String> headersh3 = new ArrayList<String>();
          for(int i=1; i?header_size; i++)
     {
          String suggestion_name_xp = "("+h3_header+")"+"["+i+"]";
            String header =rm.GetText(suggestion_name_xp);
            System.out.println(header); //Print the name of headers
headersh3.add(header); //storing h3 main content headers to the list
      }
               logger.log(LogStatus.PASS, "All main content h3 headers list printed on console");
               int headers_count = headersh3.size();
               if(headers_count==4)
               {
               logger.log(LogStatus.PASS, "Validate if the main content h3 header count is as per business specification");
               }
              Thread.sleep(2000);
               }
               catch(NoSuchElementException e)
                    {
                           logger.log(LogStatus.FAIL, "Error while printing h3 headers list on console");
                     }
                extent.endTest(logger);
extent.flush();
sm.SendAutomatedMail();
}
}

TestScripts.java

This is the actual test case where:

  • We are launching the URL.
  • We are clicking on the download tab and verifying that the download link is clickable or not.
  • We are reading all the h3 headers on the download Tab of the page.
  • We are validating the count of the h3 headers.

Reusable Components


package ScreenShotLoc;

public interface ScreenShotLocations
{
     String screenshotAfterLaunchingURL= "d:\\PhantomJSSample\\screenshotAfterLaunchingURL.jpeg";
     String screenshotAfterClickingDownload= "d:\\PhantomJSSample\\screenshotAfterClickingDownload.jpeg";
     String extentReportLoc= "d:\\PhantomJSSample\\";
     String ChromedriverLoc= "D:\\chromedriver\\chromedriver.exe";
     String PhantomJSdriverLoc= "D:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe";

}

ScreenShotLocations.java


APPURL = https://www.google.com
ALTERNATE_APPURL = https://selenium.dev/

APPURL.properties


Download_Tab = Download
H3_Headerlist= (//div[@id='mainContent']//h3[1])

ObjectRepository.properties

These are the inputs fed to this framework as the framework is designed to be data-driven.

  • ScreenShotLoc.java stores screenshot location in the drive and the driver location of the browser.
  • APPURL.properties stores the application URL involved in the testing.
  • ObjectRepository.properties stores web element locators.

Post-Execution Screenshot And Report

Now we are going to see the post-execution report:

post-execution report

Positive Scenario: The above screenshot is the report generated when all the test steps of the automated test cases are executed successfully.

Failed automated test report

Negative Scenario: The above screenshot is the report generated when all the test steps of the automated test cases are not executed successfully.

Automated Mail Screenshot:

Automated Mail Screenshot

Recommendation To Use PhantomJS As Test Browser

Given below are a few recommendations on when to use PhantomJS as a Test Browser.

  • The execution is fast with good performance.
  • A good candidate for automation if manual monitoring is not required as the browser is not GUI less.
  • Highly recommended when the test cases are designed to perform smoke tests or test cases where validation points are only taken into consideration.
  • Not recommended for regressive functional testing.

Happy Reading!!