Библиотека сайта rus-linux.net
Проект Selenium WebDriver
Автор:Simon Stewart
Перевод: Н.Ромоданов
16.5. Уровни и Javascript
Инструментальные средства автоматизации браузера, в сущности, состоят из трех частей:
- Способа взаимодействия с DOM.
- A mechanism for executing Javascript. Механизма выполнения Javascript.
- Some means of emulating user input. Некоторых средств эмуляции пользовательского ввода.
Данный раздел посвящен первой части: способу взаимодействия с DOM. Универсальным языком браузера является язык Javascript, и похоже, что это идеальный язык для использования при взаимодействии с DOM. Хотя этот выбор и кажется очевидным, если его сделать, то это приведет к некоторым интересным проблемам и влияющим друг на друга требованиям, баланс которых, когда рассматривается Javascript, нужно соблюсти.
Как и в большинстве крупных проектов, в Selenium используется многоуровневый набор библиотек. Нижним уровнем является библиотека Closure Library от Google, в которой предоставляются примитивы и механизм модульности, благодаря которым обращение к исходным файлам сводится к минимуму. Поверх этого слоя расположена библиотека утилит, предоставляющая функции, варьирующиеся от простых задач, таких как получение значения атрибута или определения того, будет ли элемент виден конечному пользователю, до гораздо более сложных действий, таких как моделирование щелчка кнопки мыши с помощь синтезируемых событий. В рамках проекта они рассматриваются как наименьшие единицы браузерной автоматизации, и поэтому называются атомами браузерной автоматизации Browser Automation Atoms или просто атомами. Наконец, есть уровни адаптеров, на которых атомы собираются в соответствие с контрактами как WebDriver, так и Core.
Рис. 16.3: Уровни библиотеки Javascript в Selenium
Библиотека Closure Library была выбрана по нескольким причинам. Главным было то, что механизм модульности, используемый в библиотеке, воспринималась компилятором Closure Compiler. Компилятор Closure Compiler - это компилятор, выходным языком которого является Javascript. "Компиляция" может быть простой, например, упорядочивание входных файлов в порядке их обращения к зависимостям, объединение их и выдачи их на печать в красивом виде, или может быть сложной, например, максимальное сжатие размеров файлов и удаление недостижимых кусков кода. Другим неоспоримым преимуществом было то, что некоторые члены команды, работающие с кодом на Javascript, были хорошо знакомы с библиотекой Closure Library.
Эта библиотека «атомов» кода используется везде во всем проекте, когда нужно обратиться к DOM. Для RC и тех драйверов, которые созданы, в основном, на Javascript, библиотека используется напрямую и компилируется, как правило, в виде монолитного скрипта. Для драйверов, написанных на Java, отдельные функции, находящиеся в слое адаптера WebDriver, компилируются с включенной полной оптимизацией, а сгенерированный Javascript добавляется в JAR архивы в виде ресурсов. Для драйверов, написанных на вариантах C, например, драйверов iPhone и IE, не только отдельные функции компилируются с полной оптимизацией, но и весь результат конвертируется в константу, указываемую в заголовке, которую можно по требованию выполнить с помощью обычного механизма выполнения Javascript, имеющегося в драйвере. Хотя все эти действия и кажутся странным, но они позволяют поместить Javascript в драйвер, лежащий ниже, и пропадает необходимость во многих местах добавлять соответствующий код.
Поскольку атомы используются везде, где можно, легко и быстро обеспечивается согласованное поведение различных браузеров, а поскольку библиотека написана на Javascript, ей для выполнения в цикле разработки не требуется повышенных привилегий. Библиотека Closure Library может загружать зависимости динамически, поэтому разработчику Selenium нужно только написать тест и загрузить его в браузер, по мере необходимости изменяя код и нажимая кнопку обновления. После того, как тест проходит в одном браузере, его можно легко загрузить в другой браузер и убедитесь, что он проходит и в нем. Поскольку в библиотеке Closure Library хорошо реализовано абстрагирование от различий в браузерах, часто достаточно знать, и это отрадно, что есть непрерывно создаваемые сборки, которые будут выполнять тестовый набор в каждом поддерживаемом браузере.
Первоначально в Core и WebDriver было много фрагментов совпадающего кода - кода, который выполнял одну и ту же функцию, но немного по-разному. Когда мы начинали работу над атомами, этот код был детально просмотрен с целью найти для этих функций "лучший вариант реализации". В конце концов, оба проекта уже широко использовались и их код был очень надежен, так что было бы не только расточительно, но и глупо выбросить все и все начать с нуля. Когда были выделены атомы, были определены места, где каждый из них должен был использоваться, и мы перешли использование атомов. Например, в методе getAttribute драйвера Firefox число строк было сокращено примерно с 50 до 6, включающих пустые строки:
FirefoxDriver.prototype.getElementAttribute = function(respond, parameters) { var element = Utils.getElementAt(parameters.id, respond.session.getDocument()); var attributeName = parameters.name; respond.value = webdriver.element.getAttribute(element, attributeName); respond.send(); };
Во второй от конца строке, где переменной respond.value присваивается значение, используется библиотека атомов WebDriver.
Атомы являются практической демонстрацией нескольких аспектов, касающихся архитектуры проекта. Естественно, соблюдается требование, что реализация API полностью соответствовала реализации языка Javascript. Еще лучше то, что одна и та же библиотека используется во всем коде; если одна и та же ошибка будет выявлена во многих реализациях, то теперь ее достаточно исправить в одном месте, что снижает затраты на изменение при одновременном повышении стабильности и эффективности. Атомы также делают в проекте более удобной работу с ошибками. Поскольку для того, чтобы проверить, что исправление работает, можно использовать обычные тестовые юниты Javascrip, барьер для присоединения к работе над проектом с открытым кодом значительно ниже, чем в случае, когда нужно знать, как реализован каждый драйвер.
Есть еще одно преимущество использования атомов. Для команд разработчиков, которые хотят в контролируемом режиме перейти к самым новым интерфейсам WebDriver API, важным инструментом является слой, эмулирующий существующую реализацию RC, но поддержка которого осуществляется драйвером WebDriver. Поскольку Selenium Core атомизирован, можно компилировать каждую функцию по отдельности, что делает задачу написания этого эмулирующего слоя более простой в реализации и более аккуратной.
Само собой разумеется, что есть и отрицательные стороны подхода. Самое важное, что компиляция Javascript в константу const языка C выполняется очень странно и это всегда сбивает с толку новых участников проекта, которые хотят работать в коде на языке C. Также очень редко, у кого из разработчика есть все версии всех браузеров, и редко, кто запускает все тесты на всех этих браузерах — возможно, что для кого-то это может в неожиданном месте ненароком привести к проблеме и в частности в тех случаях, когда наслаиваются новые непрерывно создаваемые сборки, может потребоваться некоторое время для того, чтобы идентифицировать проблему.
Поскольку атомы нормализуют значения, возвращаемые браузерами, возвращаемые значения также могут оказаться неожиданными. Например, рассмотрим следующий фрагмент HTML:
<input name="example" checked>
Значение атрибута checked будет зависеть от используемого браузера. Атомы нормализуют его, а также другие логические атрибуты, определяемые в спецификации HTML5, так, чтобы они были равны «true» или «false». Когда этот атом был добавлен в код, мы обнаружили много мест, где принимались решения, зависящие от браузера, о том, каким должно быть возвращаемое значение. Хотя в настоящее время эти возвращаемые значения совместимы, мы в течение длительного периода объяснили сообществу, что произошло и почему.
Продолжение статьи - Дистанционный драйвер и, в частности, драйвер Firefox.