В предыдущей статье я объяснил, чем отличается эмуляция действий пользователя в FirefoxDriver при помощи нативных и синтезированных событий. И теперь пришло время ответить на вопрос – какой способ лучше?
Многие считают, что нативные события лучше, потому что они более точно эмулируют поведение пользователя.
Один из основных аргументов, подкрепляющих эту точку зрения, звучит так: “а вдруг синтезированные события синтезированы неправильно – каких-то не хватает, какие-то лишние, нарушен порядок”.
Не исключено, что так оно и есть :)
Однако, во-первых, при генерации синтезированных событий Selenium использует специальный интерфейс nsIDOMWindowUtils, который предназначен как раз для эмуляции действий пользователя. Разработчики браузера Firefox позаботились о том, чтобы синтезированные события выглядели достаточно натуралистично.
А во-вторых, нативные события ведь тоже эмулируются, создаются искусственно. Так что там тоже могут быть какие-то события пропущены или сгенерированы в неправильном порядке. Вероятность накосячить здесь ничуть не меньше. Нет никаких причин доверять разработчикам механизма нативных событий больше, чем разработчикам механизма синтезированных событий.
Резюмируя вышесказанное, я бы не сказал, что та или другая реализация эмулирует действия пользователя более точно.
Зато нативные события обладают целым рядом недостатков, которые не присущи синтезированным событиям.
Во-первых, для того, чтобы нативные события корректно работали, браузер должен быть в фокусе, на переднем плане.
Поэтому запустить несколько экземпляров браузера на одной машине и при этом использовать нативные события – весьма рискованное предприятие. Синтезированные события гарантируют полную изоляцию, клик, сделанный в одном браузере, ни при каких условиях не сможет попасть в другой экземпляр.
Во-вторых, нативные события перемещения мыши вызывают у браузера впечатление, что мышь и правда двигается.
Это приводит к возникновению различных “побочных эффектов”. Например, собираемся мы кликнуть по какому-нибудь элементу. Сначала Selenium наводит на него мышь, чтобы всё выглядело естественно. Двигаясь к элементу, мышь пробегает над каким-нибудь выпадающим меню. Оно, естественно, выпадает – и накрывает элемент. Однако Selenium не обращает на это внимания и кликает в том месте, где находится требуемый элемент. Но нажатие выполняется по элементу выпавшего меню, потому что именно он находится в данный момент в той точке, куда кликнула мышь. Чтобы справиться с этой проблемой, приходится строить разные костыли. Например, сначала подвигать мышь вниз, чтобы потом она наводилась на нужный элемент не сверху (где находится меню), а снизу. Сами понимаете, изящности тестам это не добавляет. А синтезированные события таких побочных эффектов не вызывают.
Вот ещё один пример такого же рода. Есть два блока div – побольше и поменьше, маленький находится внутри большого. С обоими блоками связаны обработчики нажатия, причём разные. Теперь попробуйте ответить на вопрос, что будет, если кликнуть по большому блоку? Selenium эмулирует нажатие кнопки мыши в центральной точке, и если эта точка перекрывается маленьким вложенным блоком – нативные события вызовут срабатывание обработчика маленького блока. А если центральная точка не перекрывается маленьким блоком – тогда будет вызван обработчик большого блока. И если расположение этих блоков меняется динамически, маленький блок либо оказывается внутри, либо нет (например, в зависимости от размера окна браузера) – результат попытки кликнуть по большому блоку становится труднопредсказуемым. И опять приходится делать костыли, например, кликать не в центр, а в точку, отстоящую на два пикселя от левого края блока. Оно вам надо?
Разумеется, это далеко не полный список проблем, связанных с использованием нативных событий. Я выбрал эти примеры потому, что описанные ситуации достаточно часто встречаются в реальной жизни, они не связаны с “тонкими” действиями типа наведения курсора мыши на элемент и могут проявляться даже в примитивных сценариях, где всего лишь выполняется серия кликов.
Ну и напоследок ещё один аргумент, в пользу синтезированных событий – производительность.
Сравните среднее время выполнения тестов на сервере непрерывной интеграции Selenium, где запускается один и тот же набор тестов с нативными и синтезированными событиями.
Реализация, использующая синтезированные события, работает в среднем примерно на 20% быстрее.
Поэтому я считаю, что использования нативных событий следует по-возможности избегать. Они добавляют проблем больше, чем решают. И если синтезированные события пока не позволяют выполнить какие-то действия – нужно сообщить об этом разработчикам, мы постараемся исправить этот баг.