Три способа захвата скриншотов с помощью Selenium WebDriver

При работе с автоматизированными тестами в Selenium часто приходится делать скриншот веб-страницы или ее части. Это полезно, особенно при отладке ошибок или проверке согласованности поведения приложения в разных браузерах. Скриншоты снимаются во время выполнения теста с помощью скрипта, который помогает анализировать ошибки, просматривая состояние приложения в момент сбоя.

В этой статье мы рассмотрим три способа захвата скриншотов с помощью Selenium WebDriver.

Одна из основных целей автоматизированного тестирования  —  сокращение ручного труда. Поэтому снятие скриншотов приносит много пользы. Не хотелось бы сидеть и следить за приложением каждый раз, когда выполняются тесты. Скрипт делает снимок экрана, который поможет проверить функциональность или состояние приложения после выполнения теста. Скриншоты также помогают определить, что пошло не так, когда тест-кейс оканчивается неудачей.

Скриншоты особенно полезны при выполнении headless-тестов, где не виден графический интерфейс приложения. Тем не менее Selenium захватит и сохранит скриншот , чтобы вы могли проверить его позже.

Чтобы сделать снимок экрана в Selenium применяется интерфейс TakesScreenshot. У него есть метод getScreenshotAs(), который делает скриншот и сохраняет его в указанном месте.

1. Основной синтаксис захвата скриншота видимой части веб-страницы с помощью Selenium WebDriver

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. Скриншот во время сбоя

Чтобы сделать скриншот в случае сбоя теста, воспользуемся аннотацией TestNG @AfterMethod. В ней мы применим метод GetStatus() интерфейса ITestResult, который возвращает результат теста, а в случае сбоя в его выполнении вышеприведенные команды будут применены для создания скриншотов.

Стоит упомянуть еще одно: чтобы однозначно идентифицировать файл скриншота, мы называем его именем метода тестирования, добавляемого к параметрам теста (которые передаются через поставщика данных). Для получения имени теста и параметров мы применяем методы getName() и getParameters() интерфейса ITestResult. Если вы не задействуете поставщик данных (как в случае с этой демонстрацией), то просто воспользуйтесь методом getName() для вывода имени метода тестирования.

@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"));
 } 
}

Теперь воспользуемся аннотацией @Rule JUnit.

@Rule
public ScreenShotOnFailure onFailure = new ScreenShotOnFailure();

В аннотации @Rule применим метод ScreenShotOnFailure() интерфейса MethodRule, который возвращает результат теста, и в случае сбоя воспользуемся указанными командами для создания скриншотов.

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. Слушатели

Иногда в работе инженеров по автоматизации тестирования необходимы дополнительные библиотеки для создания отчетов. В этой статье я рассмотрю пример фреймворка Allure.

Allure Framework  —  гибкий многоязычный инструмент для отчетов о тестировании, который не только показывает краткое представление всего, что было протестировано, в формате емкого веб-отчета, но и позволяет всем участникам процесса разработки извлекать максимум полезной информации из рутинного выполнения тестов. 

Во-первых, нужно создать AllureScreenShooter:

/**
 * Класс для снятия скриншотов Allure.
 */
public class AllureScreenShooter extends ExitCodeListener {

    /**
     * Класс для снятия скриншотов Allure.
     *
     * @param result - это ITestResult.
     */
    public void onTestFailure(final ITestResult result) {
        super.onTestFailure(result);
        AllureHelpers.takeScreenshot();

    }
}

Далее создадим помощники Allure, в которых разместим аннотацию @Attachment для регистрации всех необходимых действий.

Вложение в Java-коде  —  это просто метод с аннотацией @Attachment, который возвращает либо строку, либо массив byte[], который должен быть добавлен в отчет.

Если тип возвращаемого значения в методе, аннотированном @Attachment, отличается от String или byte[], мы вызываем toString() для возвращаемого значения, чтобы получить содержимое вложения. Указать точный тип MIME для каждого вложенного файла можно с помощью параметра type аннотации @Attachment, как показано выше. Однако нет необходимости явно указывать тип вложения для всех файлов, так как Allure по умолчанию анализирует содержимое вложения и автоматически определяет его тип. Как правило, тип необходимо указывать при работе с обычными текстовыми файлами.

/**
 * Класс помощников Allure.
 */
public final class AllureHelpers {
    /**
     * Прикрепление текстовой строки.
     *
     * @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;
    }

    /**
     * Прикрепление строки в формате csv.
     *
     * @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;
    }

    /**
     * Получение кода страницы в byte [ ].
     *
     * @return the byte [ ]
     */
    @SuppressWarnings("UnusedReturnValue")
    @Attachment(value = "Html source", type = "text/html", fileExtension = ".html")
    public static byte[] getPageSource() {
        return getPageSourceBytes();
    }

    /**
     * Получение скриншота в byte [ ].
     *
     * @return the byte [ ]
     */
    @SuppressWarnings("UnusedReturnValue")
    @Attachment(value = "Screenshot", type = "image/png", fileExtension = ".png")
    public static byte[] takeScreenshot() {
        return getScreenshotBytes();
    }

    /**
     * Получение скриншота 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();
    }

    /**
     * Получение скриншота 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);
    }

    /**
     * Получение байтов кода страницы byte[ ].
     *
     * @return the byte [ ]
     */
    public static byte[] getPageSourceBytes() {
        return WebDriverRunner.getWebDriver().getPageSource().getBytes(StandardCharsets.UTF_8);
    }

    /**
     * Получение байтов скриншота byte[ ].
     *
     * @return the byte [ ]
     */
    public static byte[] getScreenshotBytes() {
        return ((TakesScreenshot) WebDriverRunner.getWebDriver()).getScreenshotAs(OutputType.BYTES);
    }

    /**
     * Получение байтов скриншота byte[ ].
     *
     * @param elem the elem
     * @return the byte [ ]
     */
    public static byte[] getScreenshotBytes(final SelenideElement elem) {
        return elem.getScreenshotAs(OutputType.BYTES);
    }
}

А в основном классе нашего проекта нужно вызвать AllureScreenShooter:

/**
 * Основной веб-класс.
 */
@Listeners(AllureScreenShooter.class)
public class BaseWeb {}

В этой статье мы рассмотрели три подхода к захвату скриншотов с помощью Selenium WebDriver.

В первом случае мы увидели, как захватить сразу весь экран. Затем мы узнали, как захватить страницу с помощью TestNG и JUnit, а в третьем подходе задействовали слушатели.

Одно из главных преимуществ Allure заключается в том, что при снятии скриншотов различные веб-драйверы ведут себя по-разному. Allure абстрагирует от этой сложности и дает прозрачные результаты независимо от драйвера. Обязательно ознакомьтесь с полной документацией, чтобы узнать про все поддерживаемые функции.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Anton Smirnov: Three ways to Capture Screenshots with Selenium WebDriver

Предыдущая статья3 способа улучшить управление состоянием в React
Следующая статьяСовременное приложение выбирает… Redux, Context или Recoil?