Selenium: Снятие скриншотов на удаленной машине

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

Запуск браузера на удаленной машине

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

@BeforeTest
@Override
@Parameters({"selenium.url",  "selenium.browser"})
public void setUp(
  @Optional String url, @Optional  String browserString) throws Exception
{
  if (browserString == null) browserString = runtimeBrowserString();
  super.setUp(url, browserString);
  staticSelenium = selenium;
};

Она обращается к методу setUp базового класса SeleneseTestBase, который всегда соединяется с локальным Selenium-сервером. Поэтому сначала нам предстоит реализовать собственный механизм запуска. Как несложно догадаться, для этого нужно немного видоизменить метод setUp. В новой версии появится два дополнительных параметра – selenium.server и selenium.port:

@BeforeTest
@Parameters({"selenium.url", "selenium.browser",  "selenium.server", "selenium.port"})
public void setUp(
  @Optional String url,  @Optional String browserString,
  @Optional String server, @Optional Integer port)  throws Exception
{
  if (browserString == null) browserString =  runtimeBrowserString()
  if (url == null) url = "http://localhost/";
  if (server == null)  server = "localhost";
  if (port == null) port = 4444;
  selenium =  new DefaultSelenium(server, port, browserString, url);
  selenium.start();
  staticSelenium = selenium;
}

Теперь, когда мы готовы запустить браузер на удаленной машине (кстати, не забудьте, что перед этим там нужно стартовать Selenium-сервер!), пришло время перейти к обсуждению различных способов снятия скриншотов.

Способ первый: помещение скриншотов в папку с общим доступом

Что будет, если использовать описанный в предыдущей заметке скриншотер, при условии, что сервер Selenium работает на удаленной машине? Скриншоты будут благополучно сделаны и помещены в какое-то место там же, на удаленной машине. Куда именно? Давайте посмотрим, какой путь к файлу мы указываем при снятии скриншота:

selenium.captureScreenshot(outFile.getAbsolutePath());

Это явно неправильно. Во-первых, там структура директорий может быть совершенно другая. Во-вторых, удаленная машина может работать под управлением другой операционной системы, с другими правилами именования файлов.

Вместо этого мы будем снимать скриншот, указывая относительный путь:

selenium.captureScreenshot(outFile.getName());

Файл при этом окажется в директории, из которой запущен Selenium-сервер. Его нужно оттуда скопировать на локальную машину в директорию с отчётом о результатах тестирования. Для этого на удаленной машине нужно заранее предоставить доступ по сети к нужной директории, на локальной машине подключить эту сетевую файловую систему, и в настройках тестов указать путь к ней на локальной машине.

Именно таким образом работает ScreenshotListener_SharedDir, который можно найти в архиве, ссылка на который приведена в конце статьи. Чтобы воспользоваться этим скриншотером, нужно в конфигурационном файле TestNG указать следующие настройки (предполагается, что тесты запускаются с Windows-машины, а сетевая файловая система подключена как диск R):

<parameter
  name="selenium.local_path_to_remote_server_home"
  value="R:\\Tools\\selenium-remote-control-1.0.3\\selenium-server-1.0.3" />
<listeners>
  <listener class-name="com.thoughtworks.selenium.ScreenshotListener_SharedDir">
  </listener>
</listeners>

Не забудьте также взять обновленный SeleneseTestNgHelper!

Возможны различные варианты настройки этого скриншотера:

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

Если предоставление сетевого доступа по какой-то причине невозможно, можно модифицировать скриншотер так, чтобы он забирал файлы с удаленной машины по протоколу FTP. Для этого можно воспользоваться вспомогательной библиотекой Jakarta Commons Net.

Способ второй: использование метода captureScreenshotAsString

Поскольку нам так или иначе необходимо скопировать скриншот с удаленной машины на локальную, воникает вполне резонный вопрос – почему нельзя для передачи данных использовать уже установленное сетевое соединение с Selenium-сервером. И конечно же ответ на этот вопрос положительный – разработчики Selenium RC предусмотрели такую возможность. Для этого нужно просто вместо метода captureScreenshot, сохраняющего скриншот в файл, использовать метод captureScreenshotAsString, который кодирует снятый скриншот в формате Base64 и возвращает в виде строки. После этого на стороне клиента нужно полученную строку раскодировать обратно и сохранить в файл.

Эта схема реализована в скриншотере RemoteScreenshotListener, который также можно найти в архиве, ссылка на который приведена в конце статьи. Для раскодирования используется библиотека Jakarta Commons Codec, которая также включена в архив. Этот скриншотер не требует никаких параметров, достаточно просто покдлючить его в конфигурационном файле TestNG.

Второй способ работает несколько медленнее, потому что, во-первых, картинку приходится кодировать и раскодировать, а во-вторых, протокол Selenium RC вообще не отличается высокой скоростью передачи данных. Но поскольку мы задались целью снимать скриншоты только в случае неуспешного завершения тестов, а это (я надеюсь!) случается не слишком часто, общее снижение производительности вряд ли будет существенным.

“Почему у меня вместо скриншотов чёрные квадраты?”

Это, наверное, самая распространенная проблема, с которой приходится сталкиваться при удаленном снятии скриншотов. Проявляться она может в самых разных ситуациях, но все они схожи друг с другом – если на удаленной машине “погас экран”, значит вместо скриншота будет чёрный квадрат. Это относится и к случаю, когда удаленная машина работает совсем без монитора.

Мой личный опыт борьбы с “черными квадратами” охватывает не слишком большое количество разных ситуаций, поэтому, прежде чем давать советы, я решил посмотреть, что по этому поводу говорят умные люди. И был очень рад, когда нашел рекомендацию Патрика Лайтбоди, которая полностью подтвердила мои собственные идеи. Для тех, кто не в курсе – Патрик создатель Selenium RC, а также основатель компании BrowserMob, предлагающей услуги по размещению Selenium-тестов в облаках, как для функционального, так и для нагрузочного тестирования, а недавно начавшей предлагать услугу “быстрой проверки сайта”, которая включает в себя снятие скриншотов, и если внимательно присмотреться, можно увидеть, что там как раз работает Selenium RC. Так что его мнению в этом вопросе можно доверять, и вот что он пишет (переводить не стал, и так всё понятно):

“What we do is launch everything from under the context of a VNC session. On Windows, configure VNC to launch a session upon startup. Then make sure the user auto-logs in. Then place a .bat file in Program Files -> Startup that launches Selenium RC. It’s kind of a pain, but it’s the most reliable way I’ve found for ensuring that Selenium RC starts in an environment that supports screenshots, launching IE, interacting with native events, etc.”

Короче говоря, запускайте всё в VNC-сессии, и ваши скриншоты будут вас радовать богатством красок. Патрик рекомендует RealVNC, любители бесплатных инструментов могут использовать TightVNC.

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

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

Проект Eclipse, содержащий исходный код: SeleniumScreenshots2.zip