Обзор – Не дожидаясь
by Dima

Вначале, короткий анонс – у нас появился ещё один блог. О чём он? Подробности здесь.

Наш, в общем-то, небольшой тест работает около 5 минут. Напомним, мы пока тестируем два простых объекта приложения Recruiting – Websites и Account. Собственно, объекта Account, как такового, не существует – мы объединяем в него функции, связанные с логином и логаутом.

Для справки: наш Selenium Grid находится в Украине, а приложение SalesForce находится в США. А интернет у нас – 2 Mb ADSL от Укртелеком.

Кроме Websites, в приложении Recruiting есть ещё десяток объектов, причём гораздо более сложных, чем Websites. Конечно, тест каждого из них будет выполняться по возможности параллельно с остальными, но всё равно работа тестов может оказаться неприятно долгой. Уже сейчас стоило бы подумать о том, как можно придать им ускорение.

Один из способов сократить время тестирования – не ждать загрузки всей страницы до конца. Бывает, что всё, что необходимо для тестирования страницы, грузится очень быстро, а львиная доля ожидания уходит на догрузку какой-нибудь картинки.

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

Её и будем использовать для экспериментов.

Вопросы

Если у вас есть опыт использования кэша во время тестирования – как на стороне браузера и RC, так и на стороне сервера (заголовки, которые он шлёт браузеру), – пожалуйста, поделитесь им с нами. В частности, интересно, можно ли кэшировать только те вещи, которые точно не участвуют в тесте.

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

Вопрос

Какие тут ещё могут быть недостатки с вашей точки зрения?

Что касается страницы логина SF – то мы не обнаружили в ней JS, который загружается в конце.

В общем, несмотря на минусы, дело стоящее. Поговорим о реализации.

Во-первых, чтобы не дожидаться загрузки страниц, нам придётся отказаться от использования функций Selenium, которые её ожидают. Это, в частности, функции, которые оканчиваются на ‘AndWait’, функция waitForPageToLoad() и функция open(). В наших тестах используются только функции waitForPageToLoad() и open(). Open(), на самом деле, неявно вызывает waitForPageToLoad(). Значит, нам надо приготовить замену только waitForPageToLoad(), которая будет ждать загрузки нужных нам элементов.

Как, собственно, определить, что все нужные элементы догрузились? Первое и самое простое, что приходит в голову – ожидать, пока появится самый последний из элементов. Например, на странице логина SF (Рис. 1) нам нужно всего два элемента: User Name и Password.

Рисунок 1

То есть мы можем выполнять isElementPresent(“password”) до тех пор, пока элемент не появится. Вроде бы всё просто, но у такого подхода есть большой минус: что, если SalesForce решит поменять поля User Name и Password местами? Тогда у тестов появится шанс начать ввод username до того, как поле загрузится.

Вопрос

А вы как думаете? Может быть, нужно заодно тестировать положение элементов друг относительно друга?

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

loaded = false;
while (!loaded) {
	loaded  = ( isElementPresent("password") &&  isElementPresent("username") );
}

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

Можно было бы, конечно, добавить таймаут, и ожидать некоторое время перед следующей попыткой, но мы нашли более интересный вариант. Вместо того, чтобы самим реализовывать ожидание, мы переложим это на JavaScript, который будет выполняться на стороне RC.

Разработчики Selenium подумали о нас и написали функцию waitForCondition(condition, timeout) – спасибо им! Condition – это условие на JavaScript, которое должно выполниться, чтобы waitForCondition вернула true. Если это условие не случится в течении timeout миллисекунд – функция вернёт false.

Всё, что нам осталось – подготовить условие на JavaScript, которое вернёт true в том случае, если на странице появятся все нужные элементы.

Короче, в commonActions.java файле появилась такая функция:

public boolean waitForListOfElements(ArrayList <String> locators, String timeout){
	Boolean result = false;
 
	String tempScript = "var result = false; ";
	if (locators.size()>0) {
		tempScript = tempScript+ "result = selenium.isElementPresent('"+locators.get(0)+"');";
	}
	for (int i=1; i<locators.size(); i++){
		tempScript = tempScript+ "result = result && selenium.isElementPresent('"+locators.get(i)+"');";
	}
 
	Event event = startEvent("waitForListOfElements", tempScript);
 
	result = waitForCondition(tempScript, timeout);
 
	event.setRealValue(result.toString());
	closeEventOk(event);
	return result;
}

Для того, чтобы проверить поля username и password, вызов функции будет выглядеть так:

ArrayList <String> locatorsList = new ArrayList <String>();		
locatorsList.add("username");
locatorsList.add("password");
waitForListOfElements(locatorsList, Settings.TIMEOUT);

Условие, которое будет выполняться на стороне RC, будет выглядеть так:

View Code JAVASCRIPT
{
var result = false; 
result = selenium.isElementPresent('username');
result = result && selenium.isElementPresent('password');
}

Всё? Мы тоже так сначала подумали. Оказалось, что мы попадаем на страницу логина с помощью команды Selenium open(), а она – увы! – уже содержит в себе вызов waitForPageToLoad(). И ничем её не заменишь. То есть, хочешь-не-хочешь, а open() ждёт до победного конца.

Как же заставить её кончиться побыстрее, чтобы мы смогли использовать нашу новую функцию?

Как оказалось, очень просто. waitForPageToLoad() ожидает загрузки страницы в течение timeout миллисекунд (свойство драйвера). Достаточно непосредственно перед её вызовом задать минимальное значение таймаута – 1, например, и функция open() уже через миллисекунду упадёт кверху лапками!

Вот как это происходит в commonActions.java:

seleniumInstance.setTimeout(Constants.MIN_SELENIUM_TIMEOUT);
openUrl("/");
seleniumInstance.setTimeout(Settings.TIMEOUT);

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

Это будет маленькое ручное исключение – “Дикое.. но симпатишное!”. Вы могли обратить на него внимание в примерах отчётов о тестировании Websites, находящихся в этом посте и показанного ниже на Рис. 2.



Рисунок 2 (Кликнуть на рисунке, чтобы увеличить)



Результат – при каждом логине экономится целых 5-8 секунд!

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

А что вы думаете? Есть ли смысл внедрять этот подход в следующей версии тестов?

Милый друг, поспеши,
Зря терять минут не надо!
Что не сказано – скажи
Не откладывая на год.
‘Песенка про пять минут’ из к/ф “Карнавальная ночь”


/pre

Print this post | Home

6 comments

  1. Alexei Barantsev says:

    Почему бы не сделать отдельный профиль браузера с отключенным flash-плагином?

  2. Alexander says:

    loaded = false;
    while (!loaded) {
    loaded = ( isElementPresent(“password”) && isElementPresent(“password”) );
    }

    похоже на опечатку.

  3. bear says:

    Alexei Barantsev:
    В данном примере (только на одной странице flash и он нам не нужен) это, наверное, отличное решение, спасибо за идею.

    Кстати, у меня, наверное, что-то с руками, – я попробовал два способа отключения flash в firefox, и ни один никак не повлиял на баннеры salesforce:
    1) tools -> add-ons -> plugins -> shockwave flash -> disable.
    2) Flashblock.
    Очистил кэш, перезапустил – фиг.
    А вы как отключаете?

    У способа с отключением флэша есть ещё пара недостатков:
    1) Если мы решим запустить те же тесты на опере, IE, других браузерах – придётся создавать профили и для них тоже.
    2) Чтобы отключить “ускорение”, нужно менять профиль. Отсюда вытекает
    3) нельзя использовать “ускорение” избирательно для отдельных страниц. Если на какой-то странице вдруг понадобится скриншот с участием flash, придётся выбирать между скриншотом и ускорением. Хотя Flashblock, наверное, можно настроить на нужные страницы, но это не совсем то.

  4. bear says:

    Alexander:
    Да, действительно, второй раз должен быть “username”, а не “password”.
    Исправил, спасибо!

  5. Alexei Barantsev says:

    У меня в виртуалке флеш просто не установлен, поэтому нет необходимости его отключать :)

    Ещё пара вариантов ускорения:

    1) реализовать блокировку “тяжёлого” контента, например, фильтровать его плагином к браузеру (типа баннерорезалки AdBlock) или поставить прокси между браузером и тестируемой системой.

    2) использовать Selenium 2.0 — в нём WebDriverBackedSelenium, а внутри него HtmlUnitDriver — никакого лишнего контента вообще, даже картинок не будет :) За это придётся, конечно, заплатить определённую цену (те же упомянутые скриншоты и ещё многое другое), но зато ускорение будет огого!

    3) разместить грид в штатах, поближе к тестируемой системе, чтобы быстро грузилось всё (но тут надо мерять — может быть обмен данными с селениум-сервером активный, и тогда вместо выигрыша можно оказаться в минусе).

  6. bear says:

    Alexei Barantsev:
    Selenium 2.0 – очень интересный вариант. Думаю, мы с ним скоро познакомимся поближе.
    Алексей, а вы уже работали с ним?
    Какие изменения нужны в тестах для Selenium, чтобы они смогли работать с Selenium 2.0?