Показаны сообщения с ярлыком tests. Показать все сообщения
Показаны сообщения с ярлыком tests. Показать все сообщения

понедельник, 1 декабря 2008 г.

Selenium и PHPUnit

Итак продолжая тему тестирования веб приложений, кратко опишу возможности Selenium RC в сочетании c PHPUnit.

Для написания тестов под selenium в PHPUnit существует расширение PHPUnit_Extensions_SeleniumTestCase. Это класс от которого следует наследоваться при написании тестовых случаев. При написании тестовых случаев следует учитывать, что каждый файл может содержать только один класс, т.е. например в файле FirstTest.php должен размещаться класс FirstTest.

Браузеры и платформы

При объявлении класса можно указать под какими браузерами и платформами должны выполняться тесты. Для этого служит свойство класса $browsers. Например так:


require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class FirstTest extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $browsers = array
    (
        array
        (
            'name' => 'Firefox On Linux', // имя, будет отображаться в сообщениях
            'browser' => '*firefox', // браузер
            'host' => 'localhost', // хост где запущен selenium-server
            'port' => 4444, // порт
            'timeout' => 30000, // таймаут
        ),
        array
        (
            'name' => 'Konqueror On Linux',
            'browser' => '*konqueror',
            'host' => 'localhost',
            'port' => 4444,
            'timeout' => 30000,
        ),
        array
        (
            'name' => 'Safari On Windows',
            'browser' => '*custom C\Program Files\Safari\Safari.exe -url',
            'host' => 'windows.machine',
            'port' => 4444,
            timeout => 30000,
        )
        // ...
    );
}
Так же указать браузер можно в методе setUp используя метод setBrowser

require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class FirstTest extends PHPUnit_Extensions_SeleniumTestCase
{
    public function setUp()
    {
        $this->setBrowser('*firefox');
    }
}
Если в методе setUp устанавливается браузер, то это перекроет свойство $browsers. Если вы хотите чтобы использовался набор браузеров из свойства $browsers, то не используйте метод setBrowser.

В методе setUp следует указать начальный URL используя метод setBrowserUrl


public function setUp()
{
    $this->setBrowserUrl('http//myproject.sf');
}

Вообще метод setUp используется для конфигурирования сессии Selenium RC. Для этого существует набор методов


public function setUp()
{
    $this->setBrowser('*firefox'); // устанавливает браузер
    $this->setBrowserUrl('http//myproject.sf'); // базовый URL
    $this->setHost('localhost'); // хост где запущен selenium-server
    $this->setPort(4444); // порт на котором висит selenium-server
    $this->setTimeout(30000); // таймаут соединения с selenium-server
    // кол-во секунд между командами отправляемыми Selenium RС
    $this->setSleep(1); 
}

Названия тестовых методов должны начинаться с test. Например:


require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class FirstTest extends PHPUnit_Extensions_SeleniumTestCase
{
    // ...
    public function testHomepage()
    {
        // ...
    }
}

PHPUnit_Extensions_SeleniumTestCase реализует методы утверждений и действий. Например:


public function testHomepage()
{
    $this->open('/'); // открыть корневой URL
    $this->assertTitleEquals('My Project'); // утверждение title=My Project
}
Практически ко всем методам действий и утверждений доступных в Selenium RC можно обратиться как $this->methodName(). Список доступных методов см. ниже в тексте.

Так же используя PHPUnit_Extensions_SeleniumTestCase можно выполнять Selenese тесты. Это тесты написанные в HTML файлах, с расширением .htm. В таких файлах тесты пишутся в таблицах, из трех колонок:

  • команда
  • цель
  • значение
Так же можно записывать тесты используя Selenium IDE, которая позволяет писать тесты не только в HTML формате, но и на всех доступных в Selenium RC языках. Для выполнения Selenese тестов (тестов записанных в html формате) можно воспользоваться методом runSelenese($filename), либо при объявлении тестового класса указать свойство $seleneseDirectory в котором прописать путь к тестам в html формате.

require_once 'PHPUnit/Extensions/SeleniumTestCase.php';

class SeleneseTest extends PHPUnit_Extensions_SeleniumTestCase
{
    // в этой директории рекурсивно будут искаться все файлы с расширением .htm
    public static $seleneseDirectory = 'test/selenium/html';
}

Методы PHPUnit_Extensions_SeleniumTestCase

Таблица утверждений

Утверждение Значение
assertAlertPresent Уведомляет об ошибке если не представлен alert
assertNoAlertPresent() Уведомляет об ошибке если alert представлен.
assertChecked(string $locator) Уведомляет об ошибке, если элемент найденый по $locator не отмечен (checked) для checkbox и radiobutton.
assertNotChecked(string $locator) Противоположен предыдущему.
assertConfirmationPresent() Уведомляет об ошибке если подтверждение (confirm) не представлено.
assertNoConfirmationPresent() Противоположен предыдущему.
assertEditable(string $locator) Уведомляет об ошибке если элемент найденный по $locator не редактируем (editable).
assertNotEditable(string $locator) Противоположен предыдущему.
assertElementValueEquals(string $locator, string $text) Уведомляет об ошибке, если значение элемента найденного по $locator не эквивалентно переданному $text.
assertElementValueNotEquals(string $locator, string $text) Противоположен предыдущему.
assertElementContainsText(string $locator, string $text) Уведомляет об ошибке если элемент найденый по $locator не содержит текст переданный в $text.
assertElementNotContainsText(string $locator, string $text) Противоположен предыдущему.
assertElementPresent(string $locator) Уведомляет об ошибке если элемент найденный по $locator не представлен.
assertElementNotPresent(string $locator) Противоположен предыдущему.
assertLocationEquals(string $location) Уведомляет об ошибке если текущий "location" не эквивалентен переданному $location.
assertLocationNotEquals(string $location) Противоположен предыдущему.
assertPromptPresent() Уведомляет об ошибке если prompt не представлен.
assertNoPromptPresent() Противоположен предыдущему.
assertSelectHasOption(string $selectLocator, string $option) Уведомляет об ошибке если переданная опция не доступна.
assertSelectNotHasOption(string $selectLocator, string $option) Противоположен предыдущему.
assertSelected($selectLocator, $option) Уведомляет об ошибке если переданный элемент не выбран (select списки).
assertNotSelected($selectLocator, $option) Противоположен предыдущему.
assertIsSelected(string $selectLocator, string $value) Уведомляет об ошибке если в списке не выбран элемент с переданным значением (select списки).
assertIsNotSelected(string $selectLocator, string $value) Противоположен предыдущему.
assertSomethingSelected(string $selectLocator) Уведомляет об ошибке, если опция идентифицируемая как $selectLocator не выбрана.
assertNothingSelected(string $selectLocator) Противоположен предыдущему.
assertTextPresent(string $pattern) Уведомляет об ошибке, если не представлен текст соответствующий переданному $pattern.
assertTextNotPresent(string $pattern) Противоположен предыдущему.
assertTitleEquals(string $title) Уведомляет об ошибке если заголовок страницы (title) не соответствует переданному $title.
assertTitleNotEquals(string $title) Противоположен предыдущему.
assertVisible(string $locator) Уведомляет об ошибке если элемент найденный по $locator не виден (visible).
assertNotVisible(string $locator) Противоположен предыдущему.

Доступные методы действий, более подробное описание:

  • addLocationStrategy
  • addSelection
  • allowNativeXpath
  • altKeyDown
  • altKeyUp
  • answerOnNextPrompt
  • assignId
  • break
  • captureEntirePageScreenshot
  • captureScreenshot
  • check
  • chooseCancelOnNextConfirmation
  • chooseOkOnNextConfirmation
  • click
  • clickAt
  • close
  • contextMenu
  • contextMenuAt
  • controlKeyDown
  • controlKeyUp
  • createCookie
  • deleteAllVisibleCookies
  • deleteCookie
  • doubleClick
  • doubleClickAt
  • dragAndDrop
  • dragAndDropToObject
  • dragDrop
  • echo
  • fireEvent
  • focus
  • goBack
  • highlight
  • ignoreAttributesWithoutValue
  • keyDown
  • keyPress
  • keyUp
  • metaKeyDown
  • metaKeyUp
  • mouseDown
  • mouseDownAt
  • mouseMove
  • mouseMoveAt
  • mouseOut
  • mouseOver
  • mouseUp
  • mouseUpAt
  • mouseUpRight
  • mouseUpRightAt
  • open
  • openWindow
  • pause
  • refresh
  • removeAllSelections
  • removeSelection
  • runScript
  • select
  • selectFrame
  • selectWindow
  • setBrowserLogLevel
  • setContext
  • setCursorPosition
  • setMouseSpeed
  • setSpeed
  • shiftKeyDown
  • shiftKeyUp
  • store
  • storeAlert
  • storeAlertPresent
  • storeAllButtons
  • storeAllFields
  • storeAllLinks
  • storeAllWindowIds
  • storeAllWindowNames
  • storeAllWindowTitles
  • storeAttribute
  • storeAttributeFromAllWindows
  • storeBodyText
  • storeChecked
  • storeConfirmation
  • storeConfirmationPresent
  • storeCookie
  • storeCookieByName
  • storeCookiePresent
  • storeCursorPosition
  • storeEditable
  • storeElementHeight
  • storeElementIndex
  • storeElementPositionLeft
  • storeElementPositionTop
  • storeElementPresent
  • storeElementWidth
  • storeEval
  • storeExpression
  • storeHtmlSource
  • storeLocation
  • storeMouseSpeed
  • storeOrdered
  • storePrompt
  • storePromptPresent
  • storeSelectOptions
  • storeSelectedId
  • storeSelectedIds
  • storeSelectedIndex
  • storeSelectedIndexes
  • storeSelectedLabel
  • storeSelectedLabels
  • storeSelectedValue
  • storeSelectedValues
  • storeSomethingSelected
  • storeSpeed
  • storeTable
  • storeText
  • storeTextPresent
  • storeTitle
  • storeValue
  • storeVisible
  • storeWhetherThisFrameMatchFrameExpression
  • storeWhetherThisWindowMatchWindowExpression
  • storeXpathCount
  • submit
  • type
  • typeKeys
  • uncheck
  • windowFocus
  • windowMaximize

[ читать дальше ]

четверг, 27 ноября 2008 г.

Selenium и symfony

Selenium

В symfony есть вполне удобные инструменты для создания тестов, как юнит тестов, так и функциональных тестов. Но в некоторых случаях инструментов symfony для функционального тестирования не достаточно. Например когда тестами надо покрыть функциональность частично реализованную на javascript. Или для тестирования определенной функциональности под разными браузерами. Для этих целей можно использовать Selenium.

Selenium - это набор инструментов для автоматизированного тестирования веб приложений под разными платформами. Со всеми инструментами тестирования можно ознакомиться на официальном сайте. Здесь я хочу рассмотреть Selenium Remote Control (RC) - инструмент для написания тестов для Selenium с использованием PHP Client Driver. Проще говоря - я хочу рассказать о написании тестов под Selenium используя PHP при разработке на базе symfony.

Интеграция Selenium и Symfony

В первую очередь, хотелось бы хоть как-то интегрировать selenium в symfony. В идеале конечно хотелось бы иметь удобный плагин, которым можно поделиться с сообществом. К сожалению на разработку полноценного плагина нет времени, поэтому сделаем просто небольшую заготовку, которая возможно в дальнейшем перерастет в полноценный плагин.

Итак, скачаем Selenium-Rc (на текущий момент лучше скачивать "latest nightly build", потому как у доступной сейчас версии "1.0 beta 1" есть некоторые проблемы с firefox 3). Собственно из всего архива нам потребуются selenium-server*/selenium-server.jar и selenium-php-client-driver*/PEAR/Testing. По мимо этого нам так же потребуется PHPUnit. Соответственно Testing и PHPUnit необходимо разместить так, что бы они были доступны при обращениях типа


require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
Можно их разместить в директории lib/vendor и прописать данный путь в include_path.

Создадим в нашем проекте (предпологается, что у нас создан проект и приложение frontend) плагин, скажем spSeleniumTestPlugin со следующей структурой:


plugins
  spSeleniumTestPlugin
    config
      settings.yml
    lib
      task
Теперь, создадим команду позволяющую запускать "selenium" тесты примерно следующей командой:

$ ./symfony selenium:test frontend
Для этого выполним следующую команду

$ ./symfony generate:task selenium:test --dir=plugins/spSeleniumTestPlugin/lib/task
Данная команда создаст класс-каркас для нашей команды в директории plugins/spSeleniumTestPlugin/lib/task. Так же разместим selenium-server.jar в дироектории plugins/spSeleniumTestPlugin/lib. И возьмемся за написание команды запускающей "selenium" тесты. Я не будет подробно останавливаться на создании команд для symfony, а просто выложу исходный код и немного его прокомментирую

require_once 'PHPUnit/Framework/TestSuite.php';
require_once 'PHPUnit/TextUI/TestRunner.php';

class seleniumTestTask extends sfBaseTask
{
    protected function configure()
    {
        $this->namespace        = 'selenium';
        $this->name             = 'test';
        $this->briefDescription = '';
        $this->detailedDescription = <<<EOF
The [selenium:test|INFO] task does things.
Call it with:

  [php symfony selenium:test|INFO]
EOF;
        $this->addArgument('application', sfCommandArgument::REQUIRED, 'The application name');
        $this->addArgument('directory', sfCommandArgument::OPTIONAL, 'Directory');
        $this->addOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'test');
    }

    protected function execute($arguments = array(), $options = array())
    {
        $dbManager = new sfDatabaseManager($this->configuration);
        $connection = Doctrine_Manager::connection();

        list($resource, $pipes) = $this->runSelenium(sfConfig::get('sf_selenium_port', 4444));

        $ds = DIRECTORY_SEPARATOR;
        $app = $arguments['application'];

        $testAppDir = sfConfig::get('sf_test_dir') . $ds . 'selenium' . $ds . $app;
        if ($arguments['directory'])
        {
            $testAppDir .=  $ds . $arguments['directory'];
        }

        $this->logSection('find tests in', $testAppDir);

        $files = sfFinder::type('file')
            ->ignore_version_control()
            ->follow_link()
            ->name('*Test.php')
            ->relative()
            ->in($testAppDir)
            ;

        $suite = new PHPUnit_Framework_TestSuite();

        foreach($files as $file)
        {
            $suite->addTestFile($testAppDir . $ds. $file);
        }

        $runner = new PHPUnit_TextUI_TestRunner;
        $runner->doRun($suite, array());

        proc_terminate($resource);
    }

    protected function runSelenium($port)
    {
        $ds = DIRECTORY_SEPARATOR;
        $pluginPath = realpath(dirname(__FILE__) . $ds . '..' . $ds . '..');

        $files = sfFinder::type('file')->ignore_version_control()->follow_link()->name('*selenium*server*.jar')->in($pluginPath);

        $seleniumJar = $files[0];
        $javaBin = sfConfig::get('sf_selenium_java_bin', 'java');

        $args = ' -port ' . $port;

        $cmd = escapeshellcmd($javaBin . ' -jar ' . $seleniumJar . $args);

        $dSpec = array
        (
            0 => array('pipe', 'r'),
            1 => array('pipe', 'w'),
            2 => array('pipe', 'r')
        );

        $pipes = array();
        $this->logSection('selenium', 'run');
        $resource = proc_open($cmd, $dSpec, $pipes);

        sleep(1);

        return array($resource, $pipes);
    }
}
В команде мы определяем два аргумента application и directory. Первый отвечает за то какое приложение будем тестировать, а второй не обязательный определяет в какой относительной директории искать тесты, это для того, что бы не запускать каждый раз все тесты, так как они могут выполняться достаточно долго.

Тесты по умолчанию ищутся в директории test/selenium/app_name - это для того, чтобы не смешивать их с остальными тестами (Примечание: по-хорошему надо бы реализовать интеграцию чтобы тесты могли располагаться вместе с остальными и запускаться общей для всех командой и этого добиться в общем не сложно, было бы время и желание). Так же в нашей команде определена одна не обязательная опция --env служащая для задания окружения.

Команда при выполнении пытается запуcтить selenium которого ищет в директории плагина. После запуска selenium команда собирает найденные тесты в тестовый набор и выполняет их средствами PHPUnit. Данный исходный код, можно взять как стартовую точку, для реализации под собственные нужды.

Пример теста

Для примера создадим файл test/selenium/GoogleTest.php со следующим содержанием:


setBrowser('*firefox');
        $this->setBrowserUrl('http://google.com/');
    }

    public function testGoogle()
    {
        $this->open("http://google.com/");
        $this->type("q", "hello world");
        $this->click("btnG");
        $this->waitForPageToLoad(10000);
        $this->assertRegExp("/helloworld\.ru/i", $this->getBodyText());

    }
}
Данный тест выполняет следующее, запускает браузер firefox, переходит на google.com, вводит в строку поиска (поле "q") "hello world", кликает на кнопку "btnG", ждет ответа и проверяет, что в тексте body содержится строка "helloworld.ru". А теперь запустим его

$ ./symfony selenium:test frontend
Если на выходе у вас, что-то вроде:

>> selenium  run
>> find tests in /var/workspace/sf11/example/test/selenium/backend
PHPUnit 3.2.21 by Sebastian Bergmann.

.

Time: 7 seconds


OK (1 tests)
значит все прошло успешно. Из выводимых сообщений можно понять, что тесты выполнялись 7 секунд, был выполнен 1 тест, который успешно прошел. Если у вас что-то не сработало, пишите.

На этом пока все, в следующей заметке я постараюсь описать основные возможности selenium и API PHPUnit.

[ читать дальше ]