Google Analytics

Search

To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g.

site:darrellgrainger.blogspot.com CSS selectors

will search for "CSS selectors" but only on my site.


Thursday, February 24, 2011

Generating a screen capture on exception thrown with Selenium 2

Recently, someone asked how to have Selenium 2 (WebDriver) create a screen capture if an exception is thrown. Here is how you do it...

Let's say you have the code:

WebDriver driver;

    @Before
    public void setUp() {
        driver = new FirefoxDriver();
    }

and you want to change it so the test cases will generate a screen capture file when an exception is thrown. Here is the first change:

WebDriver driver;

    @Before
    public void setUp() {
        WebDriverEventListener eventListener = new MyEventListener();
        driver = new EventFiringWebDriver(new FirefoxDriver()).register(eventListener);
    }

The second change is to create the MyEventListener class. The MyEventListener class will be:

public class MyEventListener implements WebDriverEventListener {
    // All the methods of the WebDriverEventListener need to
    // be implemented here. You can leave most of them blank.
    // For example...
    public void afterChangeValueOf(WebElement arg0, WebDriver arg1) {
        // does nothing
    }

    // ...

    public void onException(Throwable arg0, WebDriver arg1) {
        String filename = generateRandomFilename(arg0);
        createScreenCaptureJPEG(filename);
    }
}

The MyEventListener class will have 15 methods, including the two examples I have given here. The main method that you must implement if you want screen captures whenever an exception is thrown would be the onException method.

The biggest trick for this method is generating a unique filename for each exception. First thought is that the filename could be in the format "YYYY-MM-DD-HH-MM-SS.jpg". Unless you get two exception in one minute this will work okay. Unfortunately, it will be hard to figure out what the exception was unless you kept some sort of log in the code execution. You'll also have to waste time figuring out which exception goes with which date/time.

Personally, I'd use the format "YYYY-MM-DD-HH-MM-SS-message-from-the-throwable-argument.jpg". Selenium tends to throw multiple line exception messages. So you could take the first line of the message, change characters which are illegal for your file system and change them to underscores. You could also have something to set the location of the screen capture files and prepend that to the filename.

Here is the code I came up with in 2 minutes:

private String generateRandomFilename(Throwable arg0) {
        Calendar c = Calendar.getInstance();
        String filename = arg0.getMessage();
        int i = filename.indexOf('\n');
        filename = filename.substring(0, i).replaceAll("\\s", "_").replaceAll(":", "") + ".jpg";
        filename = "" + c.get(Calendar.YEAR) + 
            "-" + c.get(Calendar.MONTH) + 
            "-" + c.get(Calendar.DAY_OF_MONTH) +
            "-" + c.get(Calendar.HOUR_OF_DAY) +
            "-" + c.get(Calendar.MINUTE) +
            "-" + c.get(Calendar.SECOND) +
            "-" + filename;
        return filename;
    }

The final part is the code to actually generate the file. This is standard Robot stuff. Here is the code I whipped together a few projects back:

private void createScreenCaptureJPEG(String filename) {
  try {
   BufferedImage img = getScreenAsBufferedImage();
   File output = new File(filename);
   ImageIO.write(img, "jpg", output);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 private BufferedImage getScreenAsBufferedImage() {
  BufferedImage img = null;
  try {
   Robot r;
   r = new Robot();
   Toolkit t = Toolkit.getDefaultToolkit();
   Rectangle rect = new Rectangle(t.getScreenSize());
   img = r.createScreenCapture(rect);
  } catch (AWTException e) {
   e.printStackTrace();
  }
  return img;
 }

And that is it. Whenever an exception is thrown a file will be generate.

29 comments:

Anonymous said...

Nice Article! thanks.

Anonymous said...

very nice article. It was very helpful to me.

Simps

Ralph said...

Nice article. But I have one comment and one question.

First the comment:
- You can use AbstractWebDriverEventListener instead of WebDriverEventListener so you must must not override all the methods you do not use.

The Question:
I see you are using HtmlUnitDriver. But I never get any visible output from this driver. So how do you make HtmlUnitDriver rendering html in a GUI?

Darrell said...

Thanks for the comments Ralph. I am not sure why I used HtmlUnitDriver in my example. It does not display a GUI and therefore shouldn't be used for this example. I have changed it to FirefoxDriver.

GH said...

Great article! Thank you for sharing!

kaushal said...

I have tried this but this method is not working for me :(

Please guide/help.

kaushal said...

"onException" methid is not at all being called.
what should i do for this.

Please help me, please.

NZ said...

onException function will be called automatically when exception occur.

Make some exception using some selenium functions like VerifyXXXXX,AssertXXXXX, etc,.

If you wonder how to use those functions, refer the description of each functions in selenium package.

NX said...

Great post, thanks a lot

webuser said...

Can you please help me how i can implement this with the Remote web driver.

Darrell said...

To webuser,

This article uses Robot to capture the local screen image. If you want to capture the image of a remote computer it is not possible with Selenium 2. If you look at the EventFiringWebDriver class you will see it implements the interface TakesScreenshot. This seems to implement that EventFiringWebDriver has the getScreenshotAs() method. HOWEVER, if you look at the code you will see the EventFiringWebDriver.getScreenshotAs() methods checks to see if the underlying WebDriver supports it. If it does not it throws an exception. FirefoxDriver supports TakesScreenshot but RemoteWebDriver does not.

Bottom line, you can only do this with FirefoxDriver. Maybe you should file a feature request with the Selenium team to have them add TakesScreenshot to RemoteWebDriver, if RemoteWebDriver is using Firefox.

Ajay said...

Nice Article! thanks.
I have one use case in same please clarify for that.

I want to take screen sort for each and every event (means click on button or tab or link). Than i want to capture the page.

I want to capture my application from login to logout.
Please give me some solution.

Thanks,
Ajay

Darrell said...

Ajay, have a look at http://darrellgrainger.blogspot.ca/2012/07/creating-screen-capture-on-every-action.html

swapnil said...

where are the screenshots stored in the machine?

swapnil said...

Where are the screenshots stored on the machine? or they are within the project workspace?

Darrell said...

The createScreenCaptureJPEG() takes as input the full path to the file. If I use:

createScreenCaptureJPEG("filename.jpg");

it will save the file in the current directory, whatever that may be. If I use:

createScreenCaptureJPEG("C:\\foo\\bar\\filename.jpg");

then the file will be saved in C:\foo\bar. For convenience I have provided a method called generateRandomFilename(). You can modify this method to include a specific directory where screenshots are stored. In other words, the point of the article was not a full solution. You still need to create code to determine where the screenshot files will be saved.

Naresh Thandu said...

Hello Darrell,
In one of your reply you said that screen capture is implemented only with FirefoxDriver and for all other drivers it will throw an exception.
Is it still the same case, or is it being implemented with the other drivers.
I am getting NUlLL exception when I call 'GetScreenshot' on instance of 'EventFiringWebDriver'.
Can I get any document about the same on Selenium site?

Thanks,
Naresh

Darrell said...

I determine a lot from the source code. The documentation can always be out of date but the source code must be what the tool is currently doing. You can go to http://code.google.com/p/selenium/source/checkout to browser the source code. This page also has instructions on checking out the source code.

If I look at the Java bindings:

trunk/java/client/src/org/openqa/selenium/chrome/ChromeDriver.java
trunk/java/client/src/org/openqa/selenium/firefox/FirefoxDriver.java
trunk/java/client/src/org/openqa/selenium/ie/InternetExplorerDriver.java
trunk/java/client/src/org/openqa/selenium/iphone/IPhoneDriver.java
trunk/java/client/src/org/openqa/selenium/safari/SafariDriver.java

all now have "implements TakesScreenshot". So all the drivers now support taking a screen shot.

Naresh Thandu said...

Hello Darrel, is it implemented by RemoteWebDriver? I was looking into latest .Net bindings and I found it is not yet implemented by SafariDriver and RemoteWebDriver. Is it same with RemoteWebDriver in Java also?
Thanks,
Naresg

Darrell said...

The bindings for RemoteWebDriver in Java does not have "implements TakesScreenshot". So it does not support taking a screen capture.

Anonymous said...

Darrell, just wanted to say thank you for your blog! It's awesome!
Very useful and interesting.
You're doing great job!

Kyryll, Kiev, Ukraine.

Savi said...

This really work...Just a small changes in the code to extract the substring of complete message would help to reduce the filename :-)

Thanks Darrell!

Unknown said...
This comment has been removed by a blog administrator.
Cameron said...

I noticed you state this is for exceptions. Will this also generate a screenshot for failed assertions?

Darrell said...

Hey Cameron. It will not work if you throw an assertion. Frameworks like junit or TestNG will throw AssertionError which are different from Exception. This will only work for whenever a runtime exception is thrown.

If you are explicitly throwing an assert you could write your work assert which checks the assert condition and if false does a screen capture then throws an assert.

This code is more for when runtime exceptions occur. These are things you cannot anticipate and therefore need a general catchall structure.

Cameron said...

Thanks Darrell. Also, there are screenshots being taken when there are not any exceptions being thrown. I am getting an "unable to find element" error, but there is not a NoSuchElementException, nor is the script failing. Any thoughts? I posted in this question in another forum hoping you might respond.

http://www.seleniumtests.com/p/selenium-2-forum.html

Darrell said...

Cameron, have a look at http://darrellgrainger.blogspot.com/2012/07/creating-screen-capture-on-every-action.html. It talks about the other methods you can implement from the WebDriverEventListener. Experiment and see if one of the other methods might be able to help you. Generally, I find driver.findElement returns a NoSuchElementException and this code works. Must be something else to your code which is capturing the exception or doing something else.

Rather than have a conversation here you should post a general message on https://groups.google.com/forum/#!forum/webdriver. I monitor it and will see anything you post there.

Unknown said...

i m getting java.io.FileNotFoundException: D:\newJunoWorkspace\UCTest\ScreenShots\22_Nov_2013__12_59_51PM_192.168.110.101.png (The system cannot find the path specified) error once the Assertion fails and Image is getting Captured only when browser dies.
what is the error i m getting ?

Darrell said...

FileNotFoundException usually has nothing to do with Selenium. If you try saving the file to a directory which does not exist, if code is running as System or LocalSystem and these users do not have permission to write to the directory it will fail. Basically, look into reasons a FileNotFoundException occurs (not Selenium specific) and investigate if any of these reasons are why you code is failing. If you still have trouble, post to https://groups.google.com/forum/#!forum/webdriver for more help.