Three ways to Capture Screenshots with Selenium WebDriver.

Anton Smirnov
ITNEXT
Published in
6 min readDec 27, 2020

--

Photo by Christine Sandu

When working with automated tests using Selenium, we often need to take a screenshot of a web page or part of a web page. This can be useful, particularly when debugging test failures or verifying our application behavior is consistent across different browsers. We can take screenshots at runtime using the test script that helps us bug analysis by viewing the state of the application at the time of failure.
In this article, we’ll take a look at three ways we can capture screenshots using Selenium WebDriver.

As we know, one of the primary purposes of automation testing is to reduce manual effort. Therefore, the use of a screenshot captured during automated test runs becomes very useful. You would not want to monitor your application every time the tests are executed. The script can take a screenshot, which helps check the application functionality/state when the test execution completes. Screenshots also help you when your test case fails so that you can identify what went wrong in your script or your application.

Screenshots are beneficial, specifically in headless test execution, where you cannot see the GUI of the application. Still, Selenium will capture it by a screenshot and store it in a file so that you can verify the application later.

To take a screenshot in Selenium, we use an interface called TakesScreenshot, which enables the Selenium WebDriver to capture a screenshot and store it in different ways. It has a got a method “getScreenshotAs() ” which captures the screenshot and store it in the specified location.

1.Below is a fundamental syntax of capturing a screenshot, using Selenium WebDriver, of the currently visible part of the Web page:

File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File("c:\\tmp\\screenshot.png"));
public String captureScreen() {
String path;
WebDriver driver = new ChromeDriver();
try {
WebDriver webDriver = new Augmenter().augment(driver);
File source = ((TakesScreenshot)webDriver).getScreenshotAs(OutputType.FILE);
path = "./target/screenshots/" + source.getName();
FileUtils.copyFile(source, new File(path));
}
catch(IOException e) {
path = "Failed to capture screenshot: " + e.getMessage();
}
return path;
}

2. Take a screenshot of failure.

Now in order to take a screenshot in case of test failure, we will use the @AfterMethod annotation of TestNG. In the @AfterMethod annotation, we will use the ITestResult interface’s getStatus() method that returns the test result and in case of failure, we can use the above commands to take screenshots.
One more thing to mention here is in order to uniquely identify the screenshot file, we are naming it as the name of the test method appended with the test parameters (passed through a data-provider). For getting the test name and parameters we are using the getName() and getParameters() methods of the ITestResult interface. In case you are not using any data-provider(like in the case of this demo) then you can just have the getName() method to print the test method name.

@AfterMethod 
public void takeScreenShotOnFailure(ITestResult testResult) throws IOException {
if (testResult.getStatus() == ITestResult.FAILURE) {
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(scrFile, new File("errorScreenshots\\" + testResult.getName() + "-"
+ Arrays.toString(testResult.getParameters()) + ".jpg"));
}
}

Now in order to take a screenshot in case of test failure, we will use the @Rule annotation of JUnit.

@Rule
public ScreenShotOnFailure onFailure = new ScreenShotOnFailure();

In the @Rule annotation, we will use MethodRule interface’s ScreenShotOnFailure() method that returns the test result and in case of failure, we can use the above commands to take screenshots.

public class ScreenShotOnFailure implements MethodRule {

@Override
public Statement apply(final Statement statement, final FrameworkMethod frameworkMethod, final Object object) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
statement.evaluate();
} catch (RuntimeException throwable) {
makeScreenshotOnFailure();
throw throwable;
}
}

@Attachment("Screenshot on failure")
public byte[] makeScreenshotOnFailure() {
return ((TakesScreenshot) DriverHolder.getDriverThread()).getScreenshotAs(OutputType.BYTES);
}
};
}
}

3. Listeners.

Sometimes in the work of test automation engineers, you need to use additional libraries to create reports. In this article, I will consider the example of the Allure Framework.

Allure Framework is a flexible lightweight multi-language test report tool that not only shows a very concise representation of what has been tested in a neat web report format but allows everyone participating in the development process to extract the maximum of useful information from the everyday execution of tests. You can read more about it at the link.

First, you need to create an AllureScreenShooter:

/**
* The class Allure screen shooter.
*/
public class AllureScreenShooter extends ExitCodeListener {

/**
* The class Allure screen shooter.
*
*
@param result this is ITestResult.
*/
public void onTestFailure(final ITestResult result) {
super.onTestFailure(result);
AllureHelpers.takeScreenshot();

}
}

Next, you need to create Allure Helpers in which you can use the @Attachment annotation to register all the necessary actions.

An attachment in Java code is simply a method annotated with @Attachment that returns either a String or byte[], which should be added to the report.

If the return type in a method annotated with @Attachment differs from String or byte[] we call toString() on the return value to get attachment contents. You can specify the exact MIME type for each attached file using the type parameter of @Attachment annotation as shown above. However, there’s no need to explicitly specify attachment type for all attached files as Allure by default analyses attachment contents and can determine attachment type automatically. You usually need to specify the attachment type when working with plain text files.

/**
* The class Allure helpers.
*/
public final class AllureHelpers {
/**
* Attach text string.
*
*
@param text the text
*
@return the string
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "AllureTextReport", type = "text/plain", fileExtension = ".txt")
public static String attachText(final String text) {
return text;
}

/**
* Attach csv string.
*
*
@param csv the csv
*
@return the string
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "AllureCSVReport", type = "text/csv", fileExtension = ".csv")
public static String attachCSV(final String csv) {
return csv;
}

/**
* Get page source byte [ ].
*
*
@return the byte [ ]
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "Html source", type = "text/html", fileExtension = ".html")
public static byte[] getPageSource() {
return getPageSourceBytes();
}

/**
* Take screenshot byte [ ].
*
*
@return the byte [ ]
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "Screenshot", type = "image/png", fileExtension = ".png")
public static byte[] takeScreenshot() {
return getScreenshotBytes();
}

/**
* Take screenshot byte [ ].
*
*
@param name the name
*
@return the byte [ ]
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "{name}", type = "image/png", fileExtension = ".png")
public static byte[] takeScreenshot(final String name) {
return getScreenshotBytes();
}

/**
* Take screenshot byte [ ].
*
*
@param elem the elem
*
@return the byte [ ]
*/
@SuppressWarnings("UnusedReturnValue")
@Attachment(value = "Element screenshot", type = "image/png", fileExtension = ".png")
public static byte[] takeScreenshot(final SelenideElement elem) {
return getScreenshotBytes(elem);
}

/**
* Get page source bytes byte [ ].
*
*
@return the byte [ ]
*/
public static byte[] getPageSourceBytes() {
return WebDriverRunner.getWebDriver().getPageSource().getBytes(StandardCharsets.UTF_8);
}

/**
* Get screenshot bytes byte [ ].
*
*
@return the byte [ ]
*/
public static byte[] getScreenshotBytes() {
return ((TakesScreenshot) WebDriverRunner.getWebDriver()).getScreenshotAs(OutputType.BYTES);
}

/**
* Get screenshot bytes byte [ ].
*
*
@param elem the elem
*
@return the byte [ ]
*/
public static byte[] getScreenshotBytes(final SelenideElement elem) {
return elem.getScreenshotAs(OutputType.BYTES);
}
}

And in the base class of our project, you need to call AllureScreenShooter:

/**
* The class Base web class.
*/
@Listeners(AllureScreenShooter.class)
public class BaseWeb {}

In this article, we’ve seen three approaches to capturing screenshots using Selenium WebDriver.

In the first approach, we saw how to capture the whole screen using Selenium directly. Then we learned how to capture the page using TestNG and JUnit and also in the third approach we have considered we used listeners.

One of the main benefits of using Allure is that different WebDrivers behave differently when taking screenshots. Using Allure abstracts us away from this complexity and gives us transparent results irrespective of the driver we are using. Be sure to check out the complete documentation to see all the supported features available.

--

--

I’m a software engineer who specializes in testing and automation. My top languages are Java and Swift. My blog is https://test-engineer.tech/