Ускоряем Selenium-тесты

Все, кто использует Selenium тесты в своём билде, знают, что это достаточно дорогое удовольствие, потому что очень медленно. Из-за этого многие не запускают билд полностью перед коммитами или, вообще, запускают билд только по ночам. Итак, делаем наши тесты быстрее.

Отключаем native events


Примерная суть native events такова: когда тест натыкается на строку
driver.findElement(By.id(“someId”)).sendKeys(“hello”);
то операционке отправлятся некое событие типа “Введи в поле вооон с теми координатами слово ‘hello’”. Координаты расчитываются как-то сами собой и всё выглядит так, будто пользователь медленно вводит текст с клавиатуры. Проблема в том, что это затрачивает колоссальное количество времени, а нам это не надо.
Чтобы отключить эту фишку надо запускать Firefox с предварительно настроенным профайлом:

private WebDriver firefox() {
    final FirefoxProfile profile = new FirefoxProfile();
    profile.setEnableNativeEvents(false);
    return new FirefoxDriver(profile);
}

Вот так мы получили быстрый драйвер.
Если у Вас есть уже готовый профайл для запуска тестов, то можно создать в нём файл user.js:
user_pref("webdriver_enable_native_events", false);

Либо немного поменять метод создания драйвера

private WebDriver firefox(final String profileName) {
    final FirefoxProfile profile = new ProfilesIni().getProfile(profileName);
    profile.setEnableNativeEvents(false);
    return new FirefoxDriver(profile);
}

Отключаем анимацию


Анимация на сайте — это, конечно, вещь, но во время тестов — это зло. Приходится постоянно ждать, когда эта анимация закончится, вставлять какие-то костыли и т.д. И да, анимация сама по-себе съедает часть времени теста. Вообщем, отключаем анимацию на примере jQuery

private WebDriver initDriver() {
    final WebDriver wrappedDriver = firefox();
    final EventFiringWebDriver driver = new EventFiringWebDriver(wrappedDriver);
    driver.register(new AbstractWebDriverEventListener() {
            @Override
            public void afterNavigateTo(final String url, final WebDriver driver) {
                ((JavascriptExecutor) driver).executeScript("jQuery.fx.off = true;");
            }
            // скорее всего переопределения одного метода не хватит
            // так что переопределяем тут всякие beforeClickOn, afterNavigateBack и т.д. 
        }
    );
    return driver;
}

Дальше работаем с инстансом класса EventFiringWebDriver. В примере я переопределил только метод afterNavigateTo(), который вызывается автоматически после каждого вызова driver.get(someUrl). В реальной жизни этого явно недостаточно. Например, тест жмёт на кнопку и происходит автоматичекий редирект на другую страницу. В этом случае вызова afterNavigateTo() не произойдёт, поэтому приходится переопределять дополнительно beforeClickOn(), чтобы отключить анимацию при первом клике на какой-нибудь элемент после загрузки новой страницы.

Запускаем тесты в параллельном режиме


Изначально, я посматривал в сторону Selenium Grid, но у меня даже дэмку не получилось запустить, да и вообще grid показался мне не очень удобным: нужно запускать хаб и сервер. Поэтому запускаем тесты в двух и более броузерах средствами Maven и TestNG.
Добавляем в pom.xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.8.1</version>
    <executions>
        <execution>
            <id>run-integration-tests</id>
            <phase>integration-test</phase>
            <goals>
                <goal>test</goal>
            </goals>
            <configuration>
                <groups>integration</groups>
                <parallel>classes</parallel>
                <threadCount>2</threadCount>
            </configuration>
        </execution>
    </executions>
</plugin>

Если Вы инжектируете в тесты потоконебезопасные инстансы со scope=«singleton», то необходимо поменять на scope=«prototype». В лучшем случае на данном этапе можно отделаться только конфигурацией конфигов (например Spring'a). Если каждый ваш тест использует уникальное имя пользователя для логина, то всё должно заработать с пол-пинка.

Итоги


Раньше в проекте, над которым я работаю, исполнение Selenium-тестов занимало примерно 5 минут, теперь же — 1,5 минуты.

Для справки

Все вышеописанные действия были проведены на свяке Java+Selenium 2.8.0+Maven+TestNG+Firefox 7.0.1+Spring
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 13

    0
    в вашу копилку: Selenium без тормозов
    Докладчик: Алексей Баранцев
      +2
      Отключаем native events — и половина обработчиков JQuery перестаёт срабатывать, ага.

      Ускорять ввод текста, если это самое узкое место лучше другим способом: вводить текст «куском» — запихнули в клипборд, поставили фокус на поле ввода, послали Ctrl-C, и всё мигом вставилось.
        0
        Тьфу! Crrl-V, конечно :)
          +2
          Не понимаю, как скорость ввода может влиять на обработчики jQuery.
          Если пользоваться буфером обмена, то запуск нескольких броузеров одновременно на одном компе может привести к конфликтам, т.е. надо синхронизировать буфер, чтобы одновременно пара тестов в него не запихнули чего-нибудь своё.
            +2
            Если отключить использование нативных ивентов — будет, грубо говоря, только onChange в конце. Если вставлять через клипборд — примерно то же самое. И по скорости, и по набору событий.

            При использовании нативных ивентов на каждый вводимый символ генерируется полный набор событий — keyDown, keyPress, keyUp. Отключив их, вы полностью лишите себя возможности тестировать корректность работы, скажем, автопродолжения или валидации в процессе ввода.

            Про несколько браузеров согласен. Виртуалки рулят (с отключенной интеграцией клипборда, естественно).
              +1
              Для проверки работы валидации (и вообще для тестирования JavaScript) мы используем QUnit-тесты. Они гораздо быстрее и, следовательно, экономичнее.
                0
                Если эти штуки тестировать не надо — тогда в Selenium можно вообще врубить HtmlUnitDriver, и не будет ни браузера, ни нативных ивентов, но зато будет очень быстро.
                  +1
                  ммм, интересная логика. Как я понимаю, Вы с помощью Selenium предлагаете тестировать всё на свете и не писать юнит-тестов на JavaScript, а уж если писать юнит-тесты, то Selenium как-то и не нужен.
                  На самом деле всё гораздо проще
                  1. С помощью QUnit мы тестируем код JS, включая и валидацию. Я думаю, Вы понимаете, что для тестирования валидации вводимых пользователем данных, совсем не обязательно запускать веб-сервер, деплоить приложение и плодить selenium-тесты.
                  2. С помощью Selenium мы тестируем работу системы в целом, а не отдельного компонента. Если отказаться от Selenium'а, то очень велика вероятность, когда все юнит-тесты проходят, а приложение не работает.

                    0
                    Как раз и предлагаю — сначала на QUnit делаем модульные тесты, а потом используем в Selenium движок HtmlUnitDriver, который имеет ограниченные возможности по поддержке AJAX, но зато быстрый, и проверяем логику интегрально.
                      0
                      Мысль интересная, но уж очень сложно реализуемая. Как ни крути, но у QUnit есть недостаток: ему нужна тестовая html-страничка, которая часто представляет собой кусок продакшн страницы. Таким образом, запуская интегральный тест на qunit, мы всё же запускаем его на тестовой странице, а не на продакшн. Можно конечно извернуться и в продакшн страницу вставлять javascript для запуска qunit-тестов, но стоит ли игра свеч?!
                      Ну и к тому же, все подобные тесты закончатся на первом же сабмите формы.
                        0
                        Заходим селениумом на нужную страницу, подкачиваем из внешнего файла JS-тесты для этой страницы, селениумом заливаем их в браузер, дёргаем пускач, тесты добегают — а дальше селениум проверяет всё остальное. На следующей страничке повторяем. Естественно, делаем опцию, чтобы можно было отключать модульные тесты, когда надо прогнать только интеграционные.
              0
              А если keyUp/keyDown/keyPress обрабатываются при вводе текста, да ещё и event с буфером на 200 милисекунд?

              Из моей практики примерно 1/5 ошибок — проблемы рассинхронизации анимации, или потери цепочек в асинхронных событиях. При таких ускорениях они в принципе не ловятся тестами. Проще поднять какой-нибудь грид тестов, чем так — имхо. А за статью спасибо :)
            0
            а если вы используете capybara для тестирования, просто используете capybara-webkit вместо selenium. Прирост скорости примерно в 3 раза

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое