Thursday, April 7, 2011

Selenium - Capture Screenshot using TestNG

Selenium - Capture Screenshot using TestNG

It is always a best practice to capture a screen shot on error, so that we can easily analyze the issue.
With prior experience on Hybrid Framework using QTP, I have considered following points while creating the Screen capture logic.

1. Reduce the screen capture code redundancy.
As we write test in different methods, in-order to capture the screen shot on error, we need to wrap every test method in the following way
try { 
// test here
    } catch (Throwable e) { 
      // capture screenshot here
    }
How to over come this redundancy?
TestNG provide Listeners and Reporters through which we can generate custom reports.
In this example I am extending TestListenerAdapter, which implements ITestListener with empty methods, so that I don't have to override other methods of the interface that I am not interested.
I have decided to use onTestFailure method to capture the screen shot as it is invoked once test fails.

2. File naming convention
It is better to name the file using time stamp, so that we can easily understand when this error occurred.
Apart from this I also append the file name with IP address that can help me in identifying on which system this error occurred, assuming you are working on different browser combinations from different locations.

26_Mar_2011__02_51_33PM_171.22.0.111.png

3. Where to store the file
Store all the files in separate folder inside the working directory. I use separate folder called "ScreenShots".

C:\eclipse\MyWorkSpace\Implements23\ScreenShots\26_Mar_2011__02_51_33PM_132.22.0.111.png

4. How to view the files
I am directly inserting the file names in the test reporter, so that screen shot appear as hyper link.


5. Image file format
I prefer using PNG format.

Following is the code written using above mentioned points.

Note: I am not using "selenium.capturescreeshot" method, using custom logic to capture screenshot which I feel is easy. This will work even without selenium. I am using Selenium interface, didn't find way to refer the selenium object in the TestNG listeners.

package package1;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.*;
import org.testng.TestListenerAdapter;
import java.io.*;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.lang.*;
import java.net.*;

public class TestNGCustom extends TestListenerAdapter {
private int Count = 0;

//Take screen shot only for failed test case
@Override
public void onTestFailure(ITestResult tr) {
ScreenShot();
}

@Override
public void onTestSkipped(ITestResult tr) {
//ScreenShot();
}

@Override
public void onTestSuccess(ITestResult tr) {
//ScreenShot();
}

private void ScreenShot() {
try {

String NewFileNamePath;

/*
//Code to get screen resolution
//Get the default toolkit
Toolkit toolkit = Toolkit.getDefaultToolkit();
//Get the current screen size
Dimension scrnsize = toolkit.getScreenSize();
//Print the screen size
System.out.println ("Screen size : " + scrnsize);
*/    

//Get the dir path
File directory = new File (".");
//System.out.println(directory.getCanonicalPath()); 

//get current date time with Date() to create unique file name
DateFormat dateFormat = new SimpleDateFormat("dd_MMM_yyyy__hh_mm_ssaa");
//get current date time with Date()
Date date = new Date();
//System.out.println(dateFormat.format(date));

//To identify the system
InetAddress ownIP=InetAddress.getLocalHost();
//System.out.println("IP of my system is := "+ownIP.getHostAddress()); 

NewFileNamePath = directory.getCanonicalPath()+ "\\ScreenShots\\"+ dateFormat.format(date)+"_"+ownIP.getHostAddress()+ ".png";
System.out.println(NewFileNamePath);

//Capture the screen shot of the area of the screen defined by the rectangle
Robot robot = new Robot();
BufferedImage bi=robot.createScreenCapture(new Rectangle(1280,1024));
ImageIO.write(bi, "png", new File(NewFileNamePath));
Count++;//Assign each screen shot a number
NewFileNamePath = "ScreenShot"+ Count + "";
//Place the reference in TestNG web report 
Reporter.log(NewFileNamePath);


} 
catch (AWTException e) {
e.printStackTrace();
} 
catch (IOException e) {
e.printStackTrace();
}
}
}


Important Notice: When you implement TestNG interfaces, you need to inform about the listeners very early in the process, otherwise it will ignore your implemented interfaces. Following are the ways

Using -listener on the command line.
Using with ant.
Using in your testng.xml file.
Using the @Listeners annotation on any of your test classes.

I prefer using at test calass, attaching the screen shot for more clarity.

For more information refer TestNG documentation.

How to handle black/empty screen shots

Enjoy taking screen shots in Java...

Download the entire working code that is implemented in a project from the below link.

SeleniumWebdriver - Page objects Implementation - Part 2

---

34 comments:

  1. hi, many thanks for such a wonderful utility.

    I have an issue with the script that I am running currently.

    My test-script runs 24x7 on a VPS. On failure, it takes a screenshot of the page, but only a black output appears in the screenshot.

    can you please let me know why is this happening.

    ReplyDelete
  2. Younus,
    I think this happens when the screen is locked, make sure your terminal is not closed.

    ReplyDelete
  3. salut B.M
    je voudrais savoir quel sont les meilleurs outils de test de montée en charge dans le monde.
    moi j'utilise neoload mais il coûte cher. merci

    ReplyDelete
  4. Hello Tlili M,
    I have to use Google translate to read your text written in french.
    I feel NeoLoad is cheap when compared with HP loadrunner. There are many tools available in the market, it is based on your application complexity (Ajax, flex, silver light, third party components). If your application is constructed with simple technology you can use open source tools like Jmeter. Based on my experience open source tools are not capable of handling complex applications.

    ReplyDelete
  5. Awesome post! I tried this and it works fine, however i have a question. Is there a way to tweak this so that the Report.log is on the @Test method level in the report? A perfect example would be if you have a Report.log inside @Test method, this will show the link or log inside the method level not on the reporter output section.

    ReplyDelete
  6. Thanks Bharath.

    I have a prob,When I run the script in index.html file it added name of the screenshot as "Screenshot1" but not as a hyperlink.

    can you pls help me on this.

    ReplyDelete
  7. Hello Rama,

    Screen shot is stored in the following location

    NewFileNamePath = directory.getCanonicalPath()+ "\\ScreenShots\\"+ dateFormat.format(date)+"_"+ownIP.getHostAddress()+ ".png";
    System.out.println(NewFileNamePath);


    Hyper link is created using following code

    NewFileNamePath = "ScreenShot"+ Count + "";
    Reporter.log(NewFileNamePath);

    Do you have "ScreenShots" folder in your root directory?

    ReplyDelete
  8. Folder created in in root directory.

    But the problem is with this -
    NewFileNamePath = "ScreenShot"+ Count + "";

    Here I'm getting syntax error,insert';' to complete statement.

    ReplyDelete
  9. Bharath,

    Its working now.I was just confused.

    This is really helpful.

    bharath, can we produce graphs with testng?

    ReplyDelete
    Replies
    1. Hello Bharath/Rama, same error I am facing on Eclipse..syntax error,insert';' to complete statement.Can you tell me how you resolve it?

      In my case if I am using below code then report log shows ScreenShot but not hyperlink
      NewFileNamePath = "ScreenShot"+ Count + "";
      Reporter.log(NewFileNamePath);

      And if I use NewFileNamePath = "ScreenShot"+ Count + "";

      then above syntex error is coming.

      Delete
  10. Rama,
    No, copy the results in an excel graph manually or extract the results and create an excel for creating graphs.

    ReplyDelete
  11. Hello Bharath,
    Screenshot functionality is working fine on my local machine. But when I deploy it on my HUB machine(Linux machine) and execute it through HUDSON then it is not working and below error found in Hudson console:
    [testng] java.awt.AWTException: headless environment
    [testng] at java.awt.Robot.(Robot.java:75)
    [testng] at fbTAF.TestNGCustom.ScreenShot(TestNGCustom.java:74)
    [testng] at fbTAF.TestNGCustom.onTestSuccess(TestNGCustom.java:38)
    [testng] at org.testng.internal.Invoker.runTestListeners(Invoker.java:1800)
    [testng] at org.testng.internal.Invoker.runTestListeners(Invoker.java:1780)
    [testng] at org.testng.internal.Invoker.invokeMethod(Invoker.java:749)
    [testng] at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:846)
    [testng] at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1170)
    [testng] at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
    [testng] at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
    [testng] at org.testng.TestRunner.runWorkers(TestRunner.java:1147)
    [testng] at org.testng.TestRunner.privateRun(TestRunner.java:749)
    [testng] at org.testng.TestRunner.run(TestRunner.java:600)
    [testng] at org.testng.SuiteRunner.runTest(SuiteRunner.java:317)
    [testng] at org.testng.SuiteRunner.access$000(SuiteRunner.java:34)
    [testng] at org.testng.SuiteRunner$SuiteWorker.run(SuiteRunner.java:351)
    [testng] at org.testng.internal.thread.ThreadUtil$CountDownLatchedRunnable.run(ThreadUtil.java:147)
    [testng] at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    [testng] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    [testng] at java.lang.Thread.run(Thread.java:662)
    [testng] /var/lib/hudson/jobs/FBIB_TAF/workspace\ScreenShots\14_Mar_2012__08_56_56AM_127.0.0.1.png

    I am using Selenium-grid-1.0.8 and RC machine is WIN7 having Firefox browser. My RC machine IP is 192.168.1.164 but InetAddress ownIP=InetAddress.getLocalHost(); return 127.0.0.1 as shown in above log.

    Please help me in resolving this issue.

    ReplyDelete
  12. Thanks for information!
    BHARATH MARRIVADA

    ReplyDelete
  13. Hello,

    I ran the same scenario but unable to embed the hyperlink to the index.html report generated in old folder of test-output folder

    It just shows it as text

    Reporter output
    [a href="C:\Projects\TestArtifact-2tr\Failure_ScreenShots\testSclGrp_TopRecentNews_01_Aug_2012__11_34_07AM.png",target='_blank']ScreenShot1[/a]

    Please take a look and provide your comments

    ReplyDelete
    Replies
    1. String NewFileNamePath;

      java.awt.Dimension resolution = Toolkit.getDefaultToolkit()
      .getScreenSize();
      Rectangle rectangle = new Rectangle(resolution);

      // Get the dir path
      File directory = new File(".");
      // System.out.println(directory.getCanonicalPath());

      // get current date time with Date() to create unique file name
      DateFormat dateFormat = new SimpleDateFormat(
      "MMM_dd_yyyy__hh_mm_ssaa");
      // get current date time with Date()
      Date date = new Date();
      // System.out.println(dateFormat.format(date));

      // To identify the system
      InetAddress ownIP = InetAddress.getLocalHost();
      // System.out.println("IP of my system is := "+ownIP.getHostAddress());

      NewFileNamePath = directory.getCanonicalPath() + "\\ScreenShots\\"
      + TestCaseId + "___" + dateFormat.format(date) + "_"
      + ownIP.getHostAddress() + ".png";
      Print(NewFileNamePath);

      // Capture the screen shot of the area of the screen defined by the
      // rectangle
      Robot robot = new Robot();
      BufferedImage bi = robot.createScreenCapture(new Rectangle(
      rectangle));
      ImageIO.write(bi, "png", new File(NewFileNamePath));
      NewFileNamePath = "ScreenShot"
      + "
      ";
      // Place the reference in TestNG web report
      Reporter.log(NewFileNamePath);
      Wait(3000);

      Delete
    2. HI Bharat,

      I could not understand your response, can you please elaborate..??

      Delete
    3. In my post some of the html content was not copied properly(It is not allowing to past certain tags due to conflicts), so I have copied the latest code from my machine. Try to execute this code, hope this will resolve the issue.

      Delete
    4. Hi Bharat,

      Thanks for taking out time and responding to my queries, but still no success

      Not getting hyperlink, shows only text

      Can you please provide your email id, so that I can share you my detailed code (or you can share your's as undersigned)

      Thanking You
      Somesh bansal
      sbansal.ece@gmail.com

      Delete
    5. Hyper link is generated using following code.
      NewFileNamePath = "ScreenShot" + "";
      Reporter.log(NewFileNamePath);

      Check whether the file really getting created in the designated path (NewFileNamePath)

      Delete
  14. my bad luck :(
    unable to make out where I am making mistake

    ReplyDelete
    Replies
    1. I have sent you an email, now it should work fine.

      Delete
    2. please mail me the stuff since am also facing the same issue faced by Somesh Bansal

      Delete
  15. Thanks alot for this post. I had followed your code for taking screenshot.For me ,screenshot is getting loaded in ScreenShots folder in my workspace. But I am unable to make it as a link in Testng-XSLT report.Could you please clarify my doubt.
    In Testng-XSLT, ReporterOutput page,only text is printed as "Screenshot"

    ReplyDelete
  16. Hi Bharath,

    The problem with above code is , it captures the screenshot of whatever application I am working on.This makes me unable to continue with my other work ,with selenium running in other window.

    Also I guess the code You used to generate the Hyperlink will not work without using href.

    ReplyDelete
    Replies
    1. Hello Rakesh,

      I think there is an issue with TestNG wile parsing the escape sequence.
      Look at following link for more details.

      https://groups.google.com/forum/?fromgroups=#!topic/testng-users/9iIn-8kiYAo

      Delete
  17. The problem with thiscode is , it captures the screenshot of whatever application I am working on.This makes me unable to continue with my other work

    ReplyDelete
    Replies
    1. unable to continue with my other work ???
      Why you are not able to continue with your work?

      It silently take the screen shot of the entire desktop and store it in a file. If your application is minimized, you will get desktop screen shot.

      Delete
    2. Hi,
      thank you for posting such a beautiful concept !!!!!!!!
      I tried same concept, for me 'Snapshot1' link also coming but problem is , if i click that link i am unable to see snapshot.

      Its coming like this

      "The page cannot be found
      The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

      --------------------------------------------------------------------------------

      Please try the following:

      If you typed the page address in the Address bar, make sure that it is spelled correctly.

      Open the draft.blogger.com home page, and then look for links to the information you want.
      Click the Back button to try another link.
      Click Search to look for information on the Internet.

      HTTP 404 - File not found
      Internet Explorer "

      Please guide me how to over come this issue .....

      Thanks
      Sangeeta Mohnaty

      Delete
    3. I think there is an issue with TestNG wile parsing the escape sequence.
      Look at following link for more details.

      https://groups.google.com/forum/?fromgroups=#!topic/testng-users/9iIn-8kiYAo

      Delete
  18. public class TestListenerAdapter implements ITestListener {
    private int Count = 0;
    public void onFinish(ITestContext result) {}
    public void onStart(ITestContext result) {}
    @Override
    public void onTestFailure(ITestResult result) {
    System.out.println("Finished Executing Test: "+result.getName()+"Status: Failed"+"\n Reason:"+result.getThrowable());
    try {
    screenshot(result);
    } catch (AWTException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }


    @Override
    public void onTestSkipped(ITestResult result) {
    System.out.println("Skipped test: "+result.getName()+"Reason:"+result.getThrowable());
    }
    @Override
    public void onTestStart(ITestResult result) {
    System.out.println("Starting Test: "+result.getName());
    }
    @Override
    public void onTestSuccess(ITestResult result) {
    System.out.println("Finished Executing Test: "+result.getName()+"Status: Success.");
    }
    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
    System.out.println("Test failed but within success percentage");
    try {
    screenshot(result);
    } catch (AWTException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    private void screenshot(ITestResult result) throws AWTException{
    try {

    Calendar calendar = Calendar.getInstance();
    String screenShotsFolder ="E:\\Programming\\eclipse-java-indigo-win32-andriod" +
    "\\Eclipse Workspace\\Test Datamoat Web Apps\\test-output\\ScreenShots\\";
    String testMethodAndTestClass = result.getMethod().getMethodName() + "(" + result.getTestClass().getName() + ")";
    String filename = screenShotsFolder
    + testMethodAndTestClass + "-"
    + calendar.get(Calendar.YEAR) + "-"
    + calendar.get(Calendar.MONTH) + "-"
    + calendar.get(Calendar.DAY_OF_MONTH) + "-"
    + calendar.get(Calendar.HOUR_OF_DAY) + "-"
    + calendar.get(Calendar.MINUTE) + "-"
    + calendar.get(Calendar.SECOND) + "-"
    + calendar.get(Calendar.MILLISECOND)
    + ".png";

    //Take Scrrenshot
    File scrnsht =((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
    //Copy screenshot into a specific location
    FileUtils.copyFile(scrnsht, new File(filename));
    //Reporter.log("result");


    //Place the reference in TestNG web report

    Reporter.log(filename);





    }

    catch (IOException e) {

    e.printStackTrace();

    }

    }

    }


    But it does not working . Actually i did not get any screen shot in the location
    can any one help me?

    ReplyDelete
  19. NewFileNamePath = "ScreenShot"+ Count + "";
    Reporter.log(NewFileNamePath);

    by using this code i not able to see the link in testng reported created

    ReplyDelete
  20. Hi Bharath, I am using selenium with testng framework. Current implementation is we have created pages,in test case level we are giving whether to take screenshot by calling methods like log.pass/log.fail/log.message. But now the current requirement is we have moved the screenshot to page level. In suite level I am calling the methods , if a method is called twice I need to take screenshot for once and not for other. This I should implement without setting or passing additional variable.. Is there a way in TestNg to control the screenshot @method calling level

    ReplyDelete
  21. Hi Bharath, I am using selenium with testng framework. Current implementation is we have created pages,in test case level we are giving whether to take screenshot by calling methods like log.pass/log.fail/log.message. But now the current requirement is we have moved the screenshot to page level. In suite level I am calling the methods , if a method is called twice I need to take screenshot for once and not for other. This I should implement without setting or passing additional variable.. Is there a way in TestNg to control the screenshot @method calling level

    ReplyDelete