Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

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.

In this quick tutorial, we'll take a look at a couple of ways we can capture screenshots using Selenium WebDriver from our JUnit tests. To learn more about testing with Selenium, check out our great guide to Selenium.

2. Dependencies and Configuration

Let's start by adding the Selenium dependency to our pom.xml:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
</dependency>

As always, the latest version of this artifact can be found in Maven Central.

Now let's configure our driver to use Chrome from our unit test:

private static ChromeDriver driver;

@BeforeClass
public static void setUp() {
    System.setProperty("webdriver.chrome.driver", resolveResourcePath("chromedriver.mac"));

    Capabilities capabilities = DesiredCapabilities.chrome();
    driver = new ChromeDriver(capabilities);
    driver.manage()
      .timeouts()
      .implicitlyWait(5, TimeUnit.SECONDS);

    driver.get("http://www.google.com/");
}

As we can see, this is a pretty standard Selenium configuration for a ChromeDriver which will let us control the Chrome browser running on our local machine. We also configure the amount of time the driver should wait when searching for an element on the page to five seconds.

Finally, before any of our tests run, we open a favorite web page, www.google.com in the current browser window.

3. Take a Screenshot of the Viewable Area

In this first example, we'll take a look at the TakesScreenShot interface, which Selenium provides out-of-the-box. As the name suggests, we can use this interface for taking screenshots of the viewable area.

Let's create a simple method for taking screenshots using this interface:

public void takeScreenshot(String pathname) throws IOException {
    File src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
    FileUtils.copyFile(src, new File(pathname));
}

In this concise method, we first convert our driver into a TakesScreenshot using a cast. Then we can call the getScreenshotAs method, with the specified OutputType to create an image file.

After that, we can copy the file to any desired location using the Apache Commons IO copyFile method. Pretty cool! In just two lines we're able to capture screenshots.

Now let's see how we can use this method from a unit test:

@Test
public void whenGoogleIsLoaded_thenCaptureScreenshot() throws IOException {
    takeScreenshot(resolveTestResourcePath("google-home.png"));

    assertTrue(new File(resolveTestResourcePath("google-home.png")).exists());
}

In this unit test, we save the resulting image file to our test/resources folder using the filename google-home.png before asserting to see if the file exists.

4. Capturing an Element on the Page

In this next section, we'll take a look at how we can capture a screenshot of an individual element on the page. For this, we'll use a library called aShot, a screenshot utility library that is natively supported by Selenium 3 onwards.

Since aShot is available from Maven Central, we can just include it in our pom.xml:

<dependency>
    <groupId>ru.yandex.qatools.ashot</groupId>
    <artifactId>ashot</artifactId>
    <version>1.5.4</version>
</dependency>

The aShot library provides a Fluent API for configuring how exactly we want to capture are screenshots.

Now let's see how we can capture the logo from the Google home page from one of our unit tests:

@Test
public void whenGoogleIsLoaded_thenCaptureLogo() throws IOException {
    WebElement logo = driver.findElement(By.id("hplogo"));

    Screenshot screenshot = new AShot().shootingStrategy(ShootingStrategies.viewportPasting(1000))
      .coordsProvider(new WebDriverCoordsProvider())
      .takeScreenshot(driver, logo);

    ImageIO.write(screenshot.getImage(), "jpg", new File(resolveTestResourcePath("google-logo.png")));
    assertTrue(new File(resolveTestResourcePath("google-logo.png")).exists());
}

We start by finding a WebElement on the page using the id hplogo. Then we create a new AShot instance and set one of the builtin shooting strategies – ShootingStrategies.viewportPasting(1000). This strategy will scroll the viewport while we are taking our screenshot for a maximum of one second (1oooms).

Now we have the policy for how we want to take our screenshot configured.

When we want to capture a specific element on the page, internally, aShot will find an element's size and position and crop the original image. For this, we call the coordsProvider method and pass a WebDriverCoordsProvider class which will use the WebDriver API to find any coordinates.

Note that, by default, aShot uses jQuery for coordinate resolution. But some drivers have problems with Javascript.

Now we can call the takeScreenshot method passing our driver and logo element which will, in turn, give us a Screenshot object containing the result of our screen capture. As before, we finish our test by writing an image file and verifying its existence.

5. Conclusion

In this quick tutorial, we've seen two 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 a specific element on the page using a great utility library called aShot.

One of the main benefits of using aShot is that different WebDrivers behave differently when taking screenshots. Using aShot 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.

As always, the full source code of the article is available over on GitHub.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!