Selenium RC: обеспечение кроссбраузерности тестов

Одной из важных особенностей Selenium-а является возможность выполнения одних и тех же тестов под разными браузерами. Причем дизайн Selenium-а таков, что команды абстрагированы от конкретного браузера. Они посылают JScript-команды, которые уже оперируют скорее с DOM-структурой конкретного браузера. Как Selenium получает к ней доступ – это то, что Selenium скрывает внутри себя. И этим самым избавляет от ряда проблем. И это действительно так, если сравнить с тем, как обеспечивается работа с множеством браузеров в том же Borland SilkTest, AutomatedQA TestComplete. В этом случае, наиболее эффективным решением, обеспечивающим кросс-браузерность, являлись объекты-обертки – некий абстрактный объект браузера, который уже в зависимости от ряда опций выбирал тот или иной браузер. Но при этом интерфейс для работы с таким объектом один и тот же. Это тоже красивое решение, но оно зачастую всё равно требует своих адаптаций. Selenium (в отличие от аналогов) даже не требует наличия таких оберток. Он уже сам обертка.

Соответственно, как только у нас возникает задача разработать систему автоматизированного функционального тестирования для веб-приложения, которая должна быть приспособлена для работы с широким набором браузеров, то Selenium подходит как нельзя кстати. Что дальше? Автоматизаторы приступают к работе. Как правило, берут один какой-то браузер и пишут тесты под него. Зачастую, этим браузером оказывается Firefox, так как он лучше всего адаптирован для работы с Selenium-ом и к нему идут такие замечательные плагины, как Selenium IDE, Firebug, XPath checker и много других.

Итак, после определенного времени у нас нарабатывается определенный набор тестов, которые более-менее отлажены. И тут мы решили переключиться на другой браузер. Технически, при хорошем построении фреймворка, подобное переключение обеспечивается изменением одного-двух параметров конфигурации. Почему бы и не запустить? Запускаем ... и наслаждаемся новыми неизведанными проблемами, которые взялись из ниоткуда:

  • Тест, который раньше бежал не больше минуты, работает 4-5 минут
  • Не каждый раз браузер запускается вообще
  • Часть элементов не находится, некоторые события срабатывают по-другому
  • Некоторые тесты вообще заклинивают
  • В какой-то момент выскакивает непонятное окно и тест зависает до тех пор, пока вручную не закроем это окно

И самое обидное – всё это происходит одновременно, но визуально приложение работает одинаково, никаких ошибок Jscript-а и тому подобное.

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

В целом, Selenium под разные браузеры работает одинаково, но есть отдельные случаи, на которые стоит обратить внимание и которые надо адаптировать для разных браузеров. Некоторые из них:

  1. Загрузка/сохранение файлов
  2. Работа с модальными окнами
  3. Оптимизация локаторов и алгоритмов работы со страницей
  4. Инициация специальных событий

Это не полный перечень, но это наиболее часто встречающиеся случаи.

Итак, разберем их по отдельности.

Загрузка/сохранение файлов

Операции загрузки/сохранения файлов так или иначе затрагивают дополнительные диалоги, которые Selenium-ом не обрабатываются. Причем от браузера к браузеру эти диалоги могут различаться (просто посмотрите, как выглядит тот же диалог сохранения файла для IE, Firefox, Opera).

Допустим, для той же загрузки файла можно обойти диалог открытия, введя текст прямо в поле ввода файла. Но тут срабатывает ограничение браузеров – по-умолчанию свойство value элементов input type=file доступно только для чтения. Это известное ограничение безопасности, как раз предназначенное ограничить использование всяких «роботов» от автоматической загрузки файлов. Selenium, к сожалению, как раз относится к таким «роботам». Данное ограничение вполне разумно, так как подобные «роботы» чаще используются в деструктивных целях, а не в конструктивных.

К счастью поле ввода файла доступно для записи в Firefox, если его запустить в chrome-режиме.

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

Итак, у нас есть подготовленный Firefox, в котором мы можем делать загрузку/сохранение файлов с минимальным количеством проблем. Что же делать с остальными браузерами? Как показывает практика, лучше с ними не делать ничего. Одним из наиболее действенных методов при выполнении загрузки/сохранения файлов – конкретно операцию загрузки/сохранения выполнять под Firefox, после чего переключатсья в основной браузер. То есть, общий сценарий примерно такой:

  • Выполняем в основном браузере действия до того, как надо загрузить/сохранить файл
  • Отдельно (возможно, отдельным сеансом) запускаем Firefox в chrome-режиме и переходим на страницу, где нужно выполнить загрузку/сохранение файла
  • Выполняем требуемую операцию
  • Возвращаемся в основной браузер и продолжаем выполнение теста.

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

Работа с модальными окнами

В данном случае подразумеваются окна браузера, которые открываются как модальные окна (на уровне Jscript это результат работы метода openDialog). Если для Firefox каждое такое окно воспринимается как отдельное окно и для него отлично срабатывает метод waitForPopup, то для того же Internet Explorer то же самое модальное окно отдельно не видится. Более того, в этом месте есть риск «заклинивания» теста.

Что делать в таких случаях? Одним из универсальных методов обеспечения совместимости работы с подобными окнами в разных браузерах – открывать данные окна отдельно, используя метод openWindow. Основная трудность при этом – указать правильный URL. Зачастую он может быть фиксированным или он может быть считан из значения какого-нибудь атрибута. В крайнем случае, тот же Firefox позволяет посмотреть, с каким URL-ом открылся тот или иной модальный диалог (это можно найти в свойствах). А дальше уже надо подобрать правило, по которому формировать URL, какие части параметризировать и какие значения подставить. То есть, в итоге вместо:

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }waitForPopup( somePopupID , timeout ); selectWindow( somePopupID );{/syntaxhighlighter}

Мы получаем код:

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }openWindow( someURL, somePopupID ); selectWindow( somePopupID );{/syntaxhighlighter}

Это опять же выглядит как обходной маневр и если все-таки есть смысл проверять еще и сам факт того, что при клике на ту или иную ссылку/кнопку появляется подобный диалог, то тут можно сделать ветвление и если используемый браузер нормально обрабатывает waitForPopup, то используем данный метод. Если же нет, то используем openWindow .

Но на этом проблемы с модальными диалогами не заканчиваются. Например, в том же Internet Explorer, если во время выполнения теста выполняется действие, приводящее к закрытию модального окна, то браузер выдаст сообщение, запрашивающее подтверждение, что мы действительно хотим закрыть окно. Причем, это сообщение Selenium-ом не отлавливается. Тут есть множество вариантов. Одними из них могут быть:

  • После закрытия диалога посылаем «в эфир» код нажатия клавиши Enter. Это закроет диалог подтверждения и модальный диалог следом за ним. Но для этого тестируемое приложение должно быть активным, а это делает невозможным выполнение тестов в фоновом режиме (одно из удобств Selenium-а).
  • Использовать дополнительное средство для отлавливания определенных диалогов и закрытия их.

Оптимизация локаторов и алгоритмов работы со страницей

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

Одним из оптимизирующих решений, которое рекомендовано разработчиками Selenium-a, является использование CSS-локаторов вместо XPath. Для несложных локаторов это решение даже помогает, но в случае сложных локаторов, когда затрагивается достаточно большое количество элементов DOM-структуры, и CSS и XPath работают примерно с одинаковой скоростью.

Безусловно, одним из способов оптимизации скорости работы тестов является также рациональный подбор локаторов. Некоторые локаторы изначально работают быстрее. Тот же id или name будет куда более оптимальным, чем какой-либо замысловатый XPath. Но и этого зачастую бывает недостаточно, особенно когда речь идет об обработке целого набора элементов. Например, извлечение данных из некоторой таблицы. Даже если у каждой ячейки есть свой id и мы его можем вычислить, то общий процесс считывания все равно займет заметное время для больших таблиц.

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

Инициация специальных событий

Очень часто встречаются нестандартные элементы управления, которые реагируют на какие-то дополнительные события. Например, текстовое поле, которое при вводе текста сходу выдает некоторый список доступных элементов, так или иначе соответствующих введенному тексту. Обычно, подобное действия обрабатывается обработчиком события onkeypress элемента. Или какими-то аналогичными событиями. Для точной имитации работы с данным элементом обычного type недостаточно. Для имитации ввода еще полезно бы задействовать что-то вроде typeKeys. Но основная неприятность в том, что подобные действия срабатывают по-разному в разных браузерах. Причем для разных браузеров срабатывает разный набор команд Selenium-a. В этом случае надо либо выработать общий набор команд, которые гарантированно отработают под разными браузерами с получением того же результата. Либо же делать ветвления для действий, специфических для конкретного браузера.

Заключение

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