Selenium+TestNG: Автоматическое снятие скриншотов при неуспешном прохождении теста

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

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

В первой заметке я отвечу на, наверное, самый часто задаваемый вопрос: как сделать, чтобы при неуспешном прохождении теста автоматически снимался скриншот и добавлялся в отчет о результатах выполнения тестов. В одной из последующих заметок я расскажу о том, какие можно использовать разные способы снятия скриншотов, здесь же мы не будем этому уделять особого внимания, реализуем самый простой способ – снятие скриншота всего экрана.

Оригинальная версия SeleneseTestNgHelper

Если вы когда-нибудь в Selenium IDE указывали “Java (TestNG)” в качестве целевого формата для генерации кода, вы могли видеть заготовку теста приблизительно такого вида:

package com.example.tests;

import com.thoughtworks.selenium.*;
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.util.regex.Pattern;

public class Untitled extends SeleneseTestNgHelper {
  @Test public void testUntitled() throws Exception {
  }
}

Но если вы попробуете перенести этот код в среду разработки и скомпилировать его, вас постигнет разочарование – класса SeleneseTestNgHelper нет ни в Selenium RC, ни в TestNG. На официальных сайтах Selenium и TestNG он тоже не упоминается и загрузить его оттуда нельзя.

Тем не менее, при желании этот класс можно найти, как в уже скомпилированном виде, так и в исходном коде. Чтобы использовать его, достаточно помимо Selenium RC подключить ещё дополнительную библиотеку selenium-java-testng-helper-1.0.1.jar.

А, собственно, причём тут этот класс? Ведь я обещал рассказать про автоматическое снятие скриншотов при неуспешном прохождении теста? Всё верно, дело в том, что SeleneseTestNgHelper как раз и содержит реализацию этой функциональности! Просто добавьте вышеуказанную библиотеку в ваш проект, унаследуйте тесты от класса SeleneseTestNgHelper – и всё.

Впрочем, нет, не всё.

Улучшенная версия SeleneseTestNgHelper

Когда я сам начал пользоваться этим расширением, я обнаружил несколько неприятных особенностей, результатом борьбы с которыми явилась существенная его переделка, которую я и хочу предложить вам как альтернативу оригинальному SeleneseTestNgHelper (ссылки на скачивание – в конце статьи).

Далее – описание того, чем мой вариант SeleneseTestNgHelper отличается (в лучшую сторону, конечно :) ) от того, ссылка на который приведена выше.

1. Оригинальное расширение использует для снятия скриншотов второй экземпляр Selenium, а не тот, который управляет тестируемым приложением. И вовсе не потому, что так лучше, а потому, что когда разрабатывался SeleneseTestNgHelper, не существовало честного способа переиспользовать тот же самый экземпляр Selenium.

Такая возможность появилась лишь относительно недавно с появлением в TestNG нового интерфейса IInvokedMethodListener2 (он пока ещё не описан в официальной документации, но можно найти его описание в javadoc.

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

Чем это лучше? Во-первых, это позволяет вывести на передний план и развернуть окно браузера перед снятием скриншота, вызвав методы selenium.windowFocus() и selenium.windowMaximize(). Во-вторых, это даёт возможность использовать способ снятия скриншота не со всего экрана, а со страницы приложения, включая невидимые части, требующие скроллирования (впрочем, об этом, как я и обещал, расскажу в другой заметке).

2. Оригинальное расширение содержит жестко прописанный в коде механизм подключения ScreenshotListener. Более правильным способом является использование внешних методов подключения “слушателей” (listeners) – в командной строке, конфигурационном файле или в аннотации @Listeners.

Поэтому я удалил абсолютно ненужный метод attachScreenshotListener, а для обратной совместимости добавил классу SeleneseTestNgHelper аннотацию @Listeners({ScreenshotListener.class}). В результате автоматическое снятие скриншотов будет включено у всех тестов, которые наследуются от класса SeleneseTestNgHelper.

Кстати, такая замена жестко закодированного подключения на аннотацию тоже стала возможна лишь после того, как совсем недавно в TestNG стало возможно указывать аннотацию @Listeners у базового класса, раньше это нужно было делать у каждого тестового класса отдельно (либо использовать конфигурационный файл).

Конечно, эту аннотацию в классе SeleneseTestNgHelper тоже можно считать жестко закодированной. Но ещё раз подчёркиваю – она там оставлена для обеспечения обратной совместимости с предыдущей версией. А если вы хотите включать слушатель ScreenshotListener только для отдельных тестов, либо включать разные слушатели для разных тестов, тогда вам нужно в исходном коде класса SeleneseTestNgHelper удалить аннотацию @Listeners и сделать свои собственные настройки. Я не стал делать отдельную скомпилированную версию с отключенным слушателем, желающие обеспечить большую гибкость могут взять исходный код и доработать его самостоятельно.

Напоследок следует отметить, что сейчас в рамках Selenium 2.0 разрабатывается новая версия SeleneseTestNgHelper, оптимизированная для использования с новым движком WebDriver, а данная версия предлагается всем тем, кто использует сейчас и собирается продолжать использовать Selenium 1.x.

Скомпилированная библиотека: selenium-java-testng-helper-1.1.jar, проект Eclipse, содержащий исходный код: SeleniumScreenshoots.zip