Ryan Shillington
Ryan Shillington

Reputation: 25167

Can Selenium take a screenshot on test failure with JUnit?

When my test case fails, especially on our build server, I want to take a picture / screenshot of the screen to help me debug what happened later on. I know how to take a screenshot, but I was hoping for a way in JUnit to call my takeScreenshot() method if a test fails, before the browser is closed.

No, I don't want to go edit our bazillions of tests to add a try/catch. I could maybe, just possibly be talked into an annotation, I suppose. All of my tests have a common parent class, but I can't think of anything I can do there to solve this.

Ideas?

Upvotes: 22

Views: 30975

Answers (3)

Jeff Bowman
Jeff Bowman

Reputation: 95724

A few quick searches led me to a relevant article, "Grabbing Screenshots of Failed Selenium Tests" by Jason Lee, dated 24 Jan 2012. (Defunct original blogs.steeplesoft.com url: http://blogs.steeplesoft.com/posts/2012/grabbing-screenshots-of-failed-selenium-tests.html, now available from an archive.org mirror dated 7 Mar 2017.)

In short, he recommends creating a JUnit4 Rule that wraps the test Statement in a try/catch block in which he calls:

imageFileOutputStream.write(
    ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES));

In the comments, Leukipp links to a similar article "Performing an action when a test fails", by Thomas Sundberg, dated 8 Jul 2012, which offers a very similar @Rule but using getScreenshotAs(OutputType.FILE) which is then copied/moved to the intended destination.

Upvotes: 18

Norayr Sargsyan
Norayr Sargsyan

Reputation: 1868

If you want to take a screenshot on test failure, add this class

import java.io.File;

import java.io.IOException;

import java.util.UUID;

import org.apache.commons.io.FileUtils;

import org.junit.rules.MethodRule;

import org.junit.runners.model.FrameworkMethod;

import org.junit.runners.model.Statement;

import org.openqa.selenium.OutputType;

import org.openqa.selenium.TakesScreenshot;

import org.openqa.selenium.WebDriver;

public class ScreenShotOnFailure implements MethodRule {

    private WebDriver driver;

    public ScreenShotOnFailure(WebDriver driver){
        this.driver = driver;
    }

    public Statement apply(final Statement statement, final FrameworkMethod frameworkMethod, final Object o) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                try {
                    statement.evaluate();
                } catch (Throwable t) {
                    captureScreenShot(frameworkMethod.getName());
                    throw t;
                }
            }

            public void captureScreenShot(String fileName) throws IOException {
                File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
                fileName += UUID.randomUUID().toString();
                File targetFile = new File("./Screenshots/" + fileName + ".png");
                FileUtils.copyFile(scrFile, targetFile);
            }
        };
    }
}

And Before all tests, you should use this Rule:

@Rule
public ScreenShotOnFailure failure = new ScreenShotOnFailure(driver));

@Before
public void before() {
   ...
}

Upvotes: 3

dellsala
dellsala

Reputation: 942

If you want to quickly add this behavior to ALL your tests in the run you can use the RunListener interface to listen for test failures.

public class ScreenshotListener extends RunListener {

    private TakesScreenshot screenshotTaker;

    @Override
    public void testFailure(Failure failure) throws Exception {
        File file = screenshotTaker.getScreenshotAs(OutputType.File);
        // do something with your file
    }

}

Add the listener to your test runner like this...

JUnitCore junit = new JUnitCore();
junit.addListener(new ScreenshotListener((TakesScreenShots) webDriver));

// then run your test...

Result result = junit.run(Request.classes(FullTestSuite.class));

Upvotes: 7

Related Questions