Что делать когда:
Забить и оправдываться, что такой код не должен жить?
С такими ситуациями очень часто сталкивался и меня это не устраивало. При поиске подходящего метода/инструмента тестирования я наткнулся на Selenium. И применяю его уже более 3-х лет.
В Киеве 9-го апреля прошла конференция DevPoint, посвященная web — разработке. Организатором данного мероприятия была компания Uniweb. В рамках ее, решил поделиться впечатлением про Selenium.
Selenium состоит из множества подпроектов, но выделить хотел только три:
Selenium Core — JavaScript Framework для написание и выполнения тестов. Используется в Selenium IDE и Remote Control*.
Selenium IDE — плагин для Firefox, который позволяет записывать и воспроизводить тесты. Также может генерировать код тестов для использования в Selenium Remote Control.
Selenium Remote Control — клиент-серверная система, которая позволяет Вам управлять веб-браузерами локально, или на других компьютерах, используя практически любой язык программирования
В рамках этого доклада про Selenium Core не было времени акцентировать внимание, хотя этот проект наиболее интересен для написания тестов с нетривиальной логикой.
Преимущества Selenium IDE:
Ложка дёгтя:
Для примера взял живой старый проект, который покрыть тестами задача еще та. Это обычный интернет-магазин, который используется для внутренних оптовых закупок в одной компании, имен не называем…
Первый тест, который мы напишем будет просто авторизоваться в системе:
Рекомендации для тех, кто начинает использовать Selenium:
В системе очень важный функционал связанный с курсом валют, так как он должен задаваться вручную на каждый день. Напишем тест покрывающий эту логику:
И последних два теста, которые покрывают логику создания и редактирования заказа:
И для проверки наших тестов было намерено испорчено сохранение заказа:
Преимущества тестирования в IDE:
Минусы:
Selenium Remote Control — это http демон, который принимает команды через GET и выполняет их. API по общению с Selenium RC есть почти под все языки программирования. В данном докладе речь идет только про API для PHP, которое предоставляется c PHPUnit
Как уже говорилось ранние в Selenium IDE есть приятная опция по генерированию кода для *Unit:
Таким образом вы можете просто копировать код и выполнять его в своих PHPUnit suite.
Также в PHPUnit — Selenium есть возможность запускать тесты написанные в Selenium IDE:
Выполнение тестов в Selenium IDE и в PHPUnit отличаются своим подходом. Selenium IDE предполагает, что тесты выполняемые в suite могут быть зависимы друг от друга, в то время как в PHPUnit каждый тест не зависимым от предыдущего*. И на каждый тест PHPUnit шлет команды на переинициализацию браузера и соответственно наши тесты выполняются с ошибкой, так как они зависят от первого теста. Для решения этой проблемы была написана логика выполнения suite файла созданного в Selenium IDE.
Для решения проблемы с выполнением команд wait* нужно рассмотреть как они выполняются в PHPUnit:
Фактически мы зацикливаем выполнение команды, на определенный интервал и ждем пока наше условие не станет true. Реализация PHPUnit — Selenium посылая команду Selenium RC ждет от нее только два ответа, что все хорошо или что все плохо. Если пришел ответ ERROR, то он сразу закрывает браузер, пишет что произошла ошибка и соответственно наш цикл будет слать команды в уже закрытую сессию Selenium RC.
Код с решением этих проблем я выложил на github и не буду на нем останавливаться.
Еще из приятных вещей в Selenium RC то, что он умеет делать скриншоты при обнаружении ошибки:
Минус в том, что на скриншотах не подсвечивается место возникновения ошибки, но по тексту в PHPUnit обычно легко понять что не так.
По заявлениям разработчиков Selenium RC поддерживает следующие браузеры:
На самом деле добиться корректного выполнения в этих браузерах не всегда можно с первого раза, но об этом не в рамках этого доклада. Единственное, что отмечу в Firefox, IE и Chrome под Windows проблем практически никогда не возникает и в Safari под MacOS.
Для запуска наших тестов в разных браузерах достаточно описать в статической переменной $browsers массив с настройками доступа к Selenium RC:
- Достался хард-кодный проект непокрытый тестами;
- код желает лучшего, а времени на рефакторинг нет;
- внесение правок в одном месте нарушает работу логики в другом;
- для покрытия *Unit тестами, проще переписать проект;
- бизнес логика размыта по коду и даже по шаблонам.
Забить и оправдываться, что такой код не должен жить?
С такими ситуациями очень часто сталкивался и меня это не устраивало. При поиске подходящего метода/инструмента тестирования я наткнулся на Selenium. И применяю его уже более 3-х лет.
В Киеве 9-го апреля прошла конференция DevPoint, посвященная web — разработке. Организатором данного мероприятия была компания Uniweb. В рамках ее, решил поделиться впечатлением про Selenium.
Selenium состоит из множества подпроектов, но выделить хотел только три:
Selenium Core — JavaScript Framework для написание и выполнения тестов. Используется в Selenium IDE и Remote Control*.
Selenium IDE — плагин для Firefox, который позволяет записывать и воспроизводить тесты. Также может генерировать код тестов для использования в Selenium Remote Control.
Selenium Remote Control — клиент-серверная система, которая позволяет Вам управлять веб-браузерами локально, или на других компьютерах, используя практически любой язык программирования
В рамках этого доклада про Selenium Core не было времени акцентировать внимание, хотя этот проект наиболее интересен для написания тестов с нетривиальной логикой.
Selenium IDE
Преимущества Selenium IDE:
- Интуитивно понятный интерфейс;
- возможность записи действия пользователя;
- с написанием тестов справится любой человек понимающий как должен работать проект;
- автоматическое генерирование *Unit кода для различных языков программирования.
Ложка дёгтя:
- Только для FireFox;
- после записи действия все равно нужно “допиливать ручками”;
- нет встроенного инструмента для получения XPath элементов.
Применяем Selenium IDE на практике
Для примера взял живой старый проект, который покрыть тестами задача еще та. Это обычный интернет-магазин, который используется для внутренних оптовых закупок в одной компании, имен не называем…
Первый тест, который мы напишем будет просто авторизоваться в системе:
Рекомендации для тех, кто начинает использовать Selenium:
- Не забывать команду waitForPageToLoad;
- применять максимально чаще команды assert* и verify*;
- после waitForPopUp не забывайте команду selectPopUp;
- после закрытия popUp – selectWindow;
- четко понимайте разницу между click и clickAndWait;
- при тестирование ajax частей применяйте команды waitFor*.
В системе очень важный функционал связанный с курсом валют, так как он должен задаваться вручную на каждый день. Напишем тест покрывающий эту логику:
И последних два теста, которые покрывают логику создания и редактирования заказа:
И для проверки наших тестов было намерено испорчено сохранение заказа:
Преимущества тестирования в IDE:
- Последовательное выполнения тестов в suite;
- удобное внесение изменений в тесты.
Минусы:
- Только для FireFox;
- инициализацию данных приходится делать вручную;
- невозможность протестировать cron скрипты.
Selenium Remote Control
Selenium Remote Control — это http демон, который принимает команды через GET и выполняет их. API по общению с Selenium RC есть почти под все языки программирования. В данном докладе речь идет только про API для PHP, которое предоставляется c PHPUnit
Как уже говорилось ранние в Selenium IDE есть приятная опция по генерированию кода для *Unit:
Таким образом вы можете просто копировать код и выполнять его в своих PHPUnit suite.
Также в PHPUnit — Selenium есть возможность запускать тесты написанные в Selenium IDE:
class SeleneseTests extends PHPUnit_Extensions_SeleniumTestCase
{
public static $seleneseDirectory = '/devpoint/ide';
protected function setUp()
{
$this->setBrowser("*firefox");
$this->setBrowserUrl("test.devpoint.com.ua/");
}
}
Без напильника не обойтись…
- PHPUnit_Extensions_SeleniumTestCase не умеет интерпретировать suite файлы с Selenium IDE;
- для выполнения теста PHPUnit запускает всегда новый браузер;
- PHPUnit 3.4.x неправильно отрабатывает логику команд wait.
Выполнение тестов в Selenium IDE и в PHPUnit отличаются своим подходом. Selenium IDE предполагает, что тесты выполняемые в suite могут быть зависимы друг от друга, в то время как в PHPUnit каждый тест не зависимым от предыдущего*. И на каждый тест PHPUnit шлет команды на переинициализацию браузера и соответственно наши тесты выполняются с ошибкой, так как они зависят от первого теста. Для решения этой проблемы была написана логика выполнения suite файла созданного в Selenium IDE.
Для решения проблемы с выполнением команд wait* нужно рассмотреть как они выполняются в PHPUnit:
for ($second = 0; ; $second++) {
if ($second >= 60) $this->fail("timeout");
try {
if ("Заказы" == $this->getText("//html/body/table/tbody/tr/td[2]/form/table/tbody/tr/td")) break;
} catch (Exception $e) {}
sleep(1);
}
Фактически мы зацикливаем выполнение команды, на определенный интервал и ждем пока наше условие не станет true. Реализация PHPUnit — Selenium посылая команду Selenium RC ждет от нее только два ответа, что все хорошо или что все плохо. Если пришел ответ ERROR, то он сразу закрывает браузер, пишет что произошла ошибка и соответственно наш цикл будет слать команды в уже закрытую сессию Selenium RC.
Код с решением этих проблем я выложил на github и не буду на нем останавливаться.
Еще из приятных вещей в Selenium RC то, что он умеет делать скриншоты при обнаружении ошибки:
class ScreenshotTest extends PHPUnit_Extensions_SeleniumTestCase
{
protected $captureScreenshotOnFailure = true;
protected $screenshotPath = '/home/…/screenshots';
protected $screenshotUrl = 'http://localhost/screenshots';
}
Минус в том, что на скриншотах не подсвечивается место возникновения ошибки, но по тексту в PHPUnit обычно легко понять что не так.
Дергаем за ниточки не FireFox
По заявлениям разработчиков Selenium RC поддерживает следующие браузеры:
- chrome
- iexplore
- firefox3
- googlechrome
- konqueror
- firefox2
- safari
- opera
На самом деле добиться корректного выполнения в этих браузерах не всегда можно с первого раза, но об этом не в рамках этого доклада. Единственное, что отмечу в Firefox, IE и Chrome под Windows проблем практически никогда не возникает и в Safari под MacOS.
Для запуска наших тестов в разных браузерах достаточно описать в статической переменной $browsers массив с настройками доступа к Selenium RC:
class SeleneseTests extends PHPUnit_Extensions_SeleniumTestCase
{
public static $browsers = array(
array(
'name' => 'Firefox on Windows',
'browser' => '*firefox',
'host' => 'localhost',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'IE on Windows',
'browser' => '*iexplore',
'host' => 'localhost',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'Google Chrome on Windows',
'browser' => '*googlechrome',
'host' => 'localhost',
'port' => 4444,
'timeout' => 30000,
),
);
protected function setUp()
{
$this->setBrowserUrl("test.devpoint.com.ua/");
}
public function testSigninCase()
{
$this->open("/login/");
$this->waitForPageToLoad("");
$this->type("login", "admin");
$this->type("password", "admin");
$this->click("//input[@value='Войти']");
$this->waitForPageToLoad("30000");
$this->assertEquals("Главная", $this->getText("//html/body/table/tbody/tr/td/a"));
}
}
C чем Selenium Вам не поможет?
- Тестирование загрузки файлов;
- тестирование cron скриптов;
- нет нормального решения для Flash части (http://code.google.com/p/flash-selenium).