Установка и настройка функционального тестирования в Symfony2 с помощью Behat и Mink

Идея о том, что веб-приложения написанные на PHP нуждаются в тестировании, не нова и постепенно входит в повседневную практику разработчиков. PHPUnit стал стандартом тестирования PHP приложений, в том числе и в новом фреймворке Symfony2. В установке из symfony-standard в AcmeDemoBundle для тестирования контроллера используется именно он1. Я хочу рассказать о альтернативном пути тестирования функционала, с применением Behat и Mink, и описать подробности процесса установки и тестирования.

Установка фреймворка


Для начала нам понадобятся рабочая связка Apache + PHP 5.3, с поддержкой локальных хостов,intl, git. Не буду останавливаться на процессе их установки и настройки — в сети достаточно информации как это сделать для каждой отдельно взятой системы, к тому же у разработчика все это уже скорее всего установлено (лично я полагаю, что разработку нужно вести на локальной или тестовой машине, а не на работающем сайте)
Далее — нам понадобится скачать и настроить наш проект с GitHub. Откроем терминал, перейдем в папку, где расположены локальные сайты.

$ cd ~/Sites
$ git clone github.com/symfony/symfony-standard.git
$ cd symfony-standard


Проверяем, подходит ли Ваш Apache+PHP для работы в Symfony2, для этого используем поставляемый с фреймворком скрипт:
$ php app/check.php
Если необходимы какие либо настройки или модули — устанавливаем и настраиваем.

Скачиваем собственно фреймворк (он располагается по умолчанию в папке vendors). Для этого набираем в терминале:
$ php bin/vendors install
После этого нужно настроить отображения проекта в броузере, для чего я вношу изменения в два файла (пути у Вас могут быть иными, я работаю на Mac Os 10.6 с стандартно поставляемым apache):
— в /etc/hosts добавляю строчку, обозначающую, что проект у меня локальный, и в сети его искать нечего:
...
127.0.0.1 symfony-standard

— в /etc/apache2/extra/httpd-vhosts.conf

<VirtualHost *:80>
    DocumentRoot "/Users/Standart-User/Sites/symfony-standard/web"
    ServerName t0002.loc
	<Directory "/Users/Standart-User/Sites/symfony-standard/web">
		Options Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
		AllowOverride All
	</Directory>
</VirtualHost>

Рестартуем Apache, и проверяем результат работы в броузере, открыв страницу symfony-standard/app_dev.php. Если все сделано правильно — получаем следующую картинку:
Стартовая страница symfony-standard

А при вводе в терминале в корневой папке проекта
$ php app/console
получаем список команд фреймворка. Отмечаем для себя что в этом списке пока отсутствует команда behat после перечисления Options в блоке Available commands

На этом этапе можно считать что с установкой Symfony2 мы справились.

Установка Mink и Behat для тестирования


Так как все проекты, над которыми я работаю, лежат у меня в папке ~/Sites и я не хочу для каждой установки Symfony2 фреймворка устанавливать бандлы для тестирования — я сделал это один раз в папку ~/Sites/testsuite.behat, а затем подключаю ее содержимое в свои проекты.

Для удобства установки я создал проект на GitHub, оттуда можно его и установить:

    $ cd ~/Sites
    $ git clone https://github.com/livsi/testsuite.behat.git
    $ cd testsuite.behat


Фактически он содержит набор субмодулей и инструкцию как это все подвязать к своему рабочему проекту на Symfony2. После клонирования репозитория нам необходимо скачать субмодули. Для этого — выполняем команду:
    $ git submodule update --init


Все, с установкой набора для тестирования почти закончили. Так как активными ветками в Behat и Mink являются ветки develop, а не master — нужно перейти в них и переключиться вручную.
    $ cd ~/Sites/testsuite.behat/vendor/Behat/Behat/
    $ git checkout --track -b develop origin/develop
    $ cd ~/Sites/testsuite.behat/vendor/Behat/Mink/
    $ git checkout --track -b develop origin/develop


Подключаем testsuite.behat к проекту symfony-standart


Это наиболее быстрый этап, так как нам ничего не нужно получать из сети, только править конфигурационные файлы в проекте symfony-standart. Инструкции приложены к скачанному ранее коду, находятся в файле README.md

Открываем файл ~/Sites/symfony-standart/app/autoload.php и добавляем следующие строки (нужно подправить пути соответственно Вашим собственным) для подгрузки бандлов тестирования в проект symfony2:
// app/autoload.php
    $loader->registerNamespaces(array(
    // ...
    'Behat\BehatBundle' => '/Users/Standart-User/Sites/testsuite.behat/vendor',
    'Behat\Behat'       => '/Users/Standart-User/Sites/testsuite.behat/vendor/Behat/Behat/src',
    'Behat\Gherkin'     => '/Users/Standart-User/Sites/testsuite.behat/vendor/Behat/Gherkin/src',
    'Behat\Mink'        => '/Users/Standart-User/Sites/testsuite.behat/vendor/Behat/Mink/src',
    'Behat\MinkBundle'  => '/Users/Standart-User/Sites/testsuite.behat/vendor',
    'Goutte'            => '/Users/Standart-User/Sites/testsuite.behat/vendor/Goutte/src',
    'Zend'              => '/Users/Standart-User/Sites/testsuite.behat/vendor/Zend/library',
    'Behat\SahiClient'  => '/Users/Standart-User/Sites/testsuite.behat/vendor/Behat/SahiClient/src',
    'Buzz'              => '/Users/Standart-User/Sites/testsuite.behat/vendor/Buzz/lib',
// ...

В файле ~/Sites/symfony-standart/app/AppKernel.php включаем два основных бандла — BehatBundle и BehatMinkBundle:
// app/AppKernel.php
    if (in_array($this->getEnvironment(), array('dev', 'test'))) {
    // ...
    $bundles[] = new Behat\BehatBundle\BehatBundle();
    $bundles[] = new Behat\MinkBundle\BehatMinkBundle();
    // ...
    }

Прописываем обязательные параметры в ~/Sites/symfony-standart/app/config/config_dev.yml:
# app/config/config_dev.yml
framework:
    test:       ~

# ...

behat: ~

behat_mink:
    base_url:  http://symfony-standart/app_test.php/
    goutte:     ~   # enable both Goutte
    sahi:       ~   # and Sahi session

Проверяем, что все установлено и настроено правильно, для этого из корня проекта symfony-standart в терминале вводим:
$ php app/console
получаем список команд фреймворка, в котором теперь должна присутствовать команда behat. Это означает, что все работает и необходимые бандлы подключены.

Собственно тестирование



Для создания каркаса тестирования для бандла выполняем в консоли из корня проекта команду:
$ php app/console behat:bundle --init Acme\\DemoBundle
Она создает в папке src/Acme/DemoBundle каталог Features/

Добавляем в него файл, который назовем feature1.feature со следующим содержанием:

# language: ru

Функционал: Первый тест для AcmeDemoBundle
  Тестируем реализованные возможности в демонстрационном бандле

  Сценарий: Открыть главную страницу в dev окружении и убедиться в ее существовании
    Допустим я на странице "/app_dev.php"
    Тогда код ответа сервера должен быть 200
    И я должен видеть "Congratulations! You have successfully installed a new Symfony application."
    И я должен видеть "Welcome!"
    
  Сценарий: Перейти с главной страницы на страницу по ссылке "Run The Demo"
    Допустим я на странице "/app_dev.php"
    Если я кликаю по ссылке "Run The Demo"
    Тогда я на странице "/app_dev.php/demo/"
    И код ответа сервера должен быть 200
    И я должен видеть "Available demos" 


Это и есть те самые функциональные тесты. Чтобы запустить тест — вводим в консоли команду:
$ php app/console behat --no-paths @AcmeDemoBundle и видим примерно следующий результат:
Результат тестирования AcmeDemoBundle symfony-standard

По скриншоту можно увидеть, что синтаксис вызова упростился: сначала от behat:test:bundle до behat:bundle, а теперь и до просто behat.

Справку по необходимым ключам консольной команды можно получить, набрав
app/console help behat

Потратив относительно немного времени на подключение Behat и Mink получаем значительный профит в виде простых и читабельных, наглядных тестов, которые пишутся на понятном языке. Они быстро пишутся и быстро выполняются.

Благодарности


Особая благодарность — Константину Кудряшову (хабраюзер everzet) за создание Behat и Mink, консультации их использованию, компании KnpLabs за спонсорскую помощь и содействие их автору, за поддержку проекта и дальнейшее его совершенствование.
Также большое спасибо разработчикам Symfony2 за мощный и удобный фреймворк, и подробнейшую документацию, которая помогает быстро его освоить.

Использованы материалы и репозитарии:






Пример тестирования контроллера с помощью PHPUnit (используется по умолчанию в symfony2):

namespace Acme\DemoBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class DemoControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $client = $this->createClient();

        $crawler = $client->request('GET', '/demo/hello/Fabien');

        $this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
    }
}


То же самое с помощью Behat
  Сценарий: Повторяем тест контроллера, проводимого через PHPUnit
    Допустим я на странице "/app_dev.php/demo/hello/Fabien"
    Тогда я должен видеть "Hello Fabien"


Не правда ли — тест на Behat более нагляден?
Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 33
  • +1
    Честно говоря, я сначала глазам не поверил… Описание тестов на русском языке! Переспросил у автора: да, это действительно не шутка. И хотя пока еще одолевают двойственные чувства, но это блин, как минимум приятно!
    • 0
      Внутри просто регекспы, больше никакой магии.
    • +1
      В догонку вам: www.knplabs.com/en/blog/behat-2.0 :-)
      • 0
        Разве PHPUnit не для написания модульных тестов предназначен? Зачем на нем функциональные тесты писать?
            • 0
              Не совсем понял, выпиливаются все классы или только возможность гонять тесты из консоли PHPUnit? Почти год прошел, а сам модуль Story существует в том виде в котором был.

              Ну такое, просто не бихэтом единым. В свое время хотел перевести рабочий проект на бихет, но понял, что писать тесты на человекоподобном языке ни один наш разработчик не захочет. Да и нужно ли? Слишком много времени будет тратится на отладку, написание правильных конструкций и правку орфографических ошибок. Сейчас использую надстройку над PHPUnit BDD и вроде всё устраивает.

              Короче, Behat вещь специфическая, но в свете отсутствия альтернативы подается как единственно правильная.
              • 0
                «PHPUnit's BDD functionality is deprecated in PHPUnit 3.5 and will be removed in PHPUnit 3.6.»

                docs.behat.org/cookbook/behat_and_mink.html
                • 0
                  Не спорю, минк клёвый. Тогда вопрос: насколько сложно архитектурно начать использовать Behat без Gherkin?
                  • 0
                    Вопрос крайне странный. Behat — это и есть Gherkin! Ваша неприязнь к средству еще не говорит о том, что вам нельзя попробовать его использовать, верно? Мне сложно спорить с вашим «не нравится», пока у вас не появится вменяемых причин, а для этого надо хотя бы попытаться.

                    И по поводу ваших предыдущих тезисов:

                    1. «Слишком много времени будет тратится на написание правильных конструкций и правку орфографических ошибок» ©
                    Из коробки:
                    image
                    2. «Слишком много времени будет тратится на отладку» ©
                    Из коробки:
                    image

                    Вы извините, но я крайне сомневаюсь, что вы сможете на PHPUnit функционально оттестировать вашу страницу настолько же быстро как с Behat (см. выше). И крайне сомневаюсь, что дебаг сценарных тестов в PHPUnit удобнее, чем в Behat (см. выше) ;-)
                    • 0
                      Ну просто у меня есть свое виденье того как лучше описывать сценарии. Например, хотелось бы для описания сценариев использовать тот же PHP, при если именовать функции по тому же принципу, что в bdd, то код остается понятен непрограммистам, но в то же время благодаря автодополнению пишется в разы быстрее обычного текста.

                      Вроде так
                      $i->click(3);
                      $i->click(2);
                      $i->click('+');
                      $i->shouldGet(5)

                      Если заменить Gherkin другим языком сценариев в нельзя то останусь пока на PHPUnit.
                      • 0
                        Т.е. заменить Gherkin другим языком описания сценариев никак нельзя?
                        • 0
                          Что вас не устраивает в Gherkin?
                          • 0
                            Ну я же сказал — его человечность :)
                            Например:

                            «Допустим я на странице „/app_dev.php/demo/hello/Fabien“»

                            Грамотный человек поставит после слова «допустим» запятую, а парсер это не схавает. И всё вывалится. Ну это как частный пример.

                            А суть в том, что для описания хотелось бы использовать более формальный язык, тот же PHP, например. Там большинство ошибок можно исправить на этапе написания, а большинство команд можно написать правильно сразу с помощью автодополнения.
                            Грубо говоря, смотреть всё время в список regex'ов и выискивать там нужную команду я не хочу.
                            • 0
                              У вас коренное непонимание принципов сценарного BDD, откуда явный перекос в спецификации. Почитайте dannorth.net/whats-in-a-story/ на досуге ;-)
                              • 0
                                Да, возможно я имею ввиду что-то среднее между BDD и функциональным тестированием. От первого хочу видеть возможность более явно указывать сценарий, от второго — описание теста на языке программирования.

                                Просто в моем понимании тесты пишутся программистами, а значит удобнее описывать используя всё богатство инструментариев, которое дает полноценный язык программирования.
                                • 0
                                  Просто почитайте статью.
                                  • 0
                                    Насколько я понял, тут отличия где-то в том же чем TDD отличается от написания тестов. TDD предусматривает методологию разработки, частью которой является написание тестов. То же и с BDD. Исходя из описания, BDD хорошо подходит, когда описание сценариев это совместный продукт тестера, менеджера, заказчика и программиста. Когда же сценарий пишется самим программистом для себя или «по мотивам» фичи, с целью просто покрыть функционал, то именно методология немного излишня.

                                    Скажу иначе, как программисту мне будет трудно убедить руководство использовать BDD в проекте. Но лаконично писать сценарии для своего функционала я могу и без использования самой методологии. Так же плюсом является возможность показать тестеру какие действия выполняет мой тест и почему, например, тест проходит, а его действия — нет.

                                    Т.е. у меня есть конкретная цель — покрыть проект функциональными тестами с простыми сценариями и понятными описаниями, доступными непрограммистам. Именно по этому мне нравится Behat и BDD вцелом, но тесты должны оставаться тестами, а не частью методологии разработки. За неё я не отвечаю.
                                    • 0
                                      Вы все правильно поняли.

                                      Behat — тул для BDD разработки. Вы же не используете BDD, вы просто пишете функциональные тесты. Behat без BDD — это как машина без колес, все верно ;-)
                                      • 0
                                        Ну вот, в этой хабровской заметке как раз идет речь о том, чтобы использовать Behat для функциональных тестов. Можно было бы изначально сказать, что пример малость не верен.

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

                                          «писать тесты не так, что никто не поймет что там происходит, а в более явном виде»

                                          Сценарии как раз и пишуться для того, чтобы кто угодно понял что там происходит без необходимости понимать как это реализовано ;-)
                                          • 0
                                            Да, но не обязательно писать это именно на человеческом языке. Точно так же можно писать на PHP. ПРи этом совершенно не сложно сделать так, чтобы код был понятен обычному человеку. Хотя бы тестеру.
                                            • 0
                                              Код по определению не может быть понятен обычному человеку. Ну или у нас слишком разняться понятия «обычности».

                                              Если перед вами стоит задача писать простые функциональные — никаких проблем. Пишите хоть на JAVA. Но не называйте это BDD! Когда я говорю «сценарий», я имею в виду «поведенческие сценарии», являющиеся частью методологии, а не то, что называете сценариями вы ;-)
                                              • 0
                                                Ну да, дяде васе с улицы тест понятен не будет, тут согласен. Впрочем, это и не нужно.

                                                Мне, как было сказано, пофиг каким DD это будет называться. Есть сценарии описанные простым языком. В моем примере выше РНР-код нехитрыми преобразованиями транслируется в человеческий язык и обратно.

                                                Вроде же разобрались, что я говорю конкретно о функциональных тестах. Модель BDD просто помогает упростить их спецификацию.

                                                Но когда идет речь о том, что Behat изящно заменяет именно функциональное тестирование, то тут я не согласен. Он добавляет новую методологию, а с ней и присущие ей сложности. См. заголовок статьи.
                                                • 0
                                                  «В моем примере выше РНР-код нехитрыми преобразованиями транслируется в человеческий язык и обратно.»
                                                  Если подразумевается, что никто кроме программистов этот код читать не будет, зачем эти преобразования?
                                                  А если подразумевается обратное, то, извините, но ваши описания никто кроме программиста прочитать все-равно не сможет. С хитрыми преобразованиями или без.

                                                  «Модель BDD просто помогает упростить их спецификацию.»
                                                  Методология сценарного BDD описывает разработку через поведенческие сценарии. Эти сценарии являются ни чем иным, как приемочным критерем разрабатываемых фич и могут быть использованы в качестве приемочных тестов. Но основная идея — именно в коммуникции со стэк-холдерами, продакт-овнерами, менеджерами и иже с ними.

                                                  «Но когда идет речь о том, что Behat изящно заменяет именно функциональное тестирование, то тут я не согласен. „
                                                  Изящно заменяет. Если вы итак применяете аджайл и используете что-то вроде scrum или другой историйной методологии — вы УЖЕ пишете пользовательские истории. Behat позволяет эти истории использовать в качестве функциональных тестов. Были юзер-стори, остались юзер-стори. Только теперь они исполняемы!

                                                  Если вы ватерфолите и не в зуб ногой что такое гибкая разработка, тогда да. Вам будет казаться, что Behat требует излишней дополнительной работы и вам это все не нужно.
                                                  • 0
                                                    Ох ты ж йо. При чем тут методология? Мы же вроде от неё абстрагировались.

                                                    Вернемся к простому.

                                                    Я пишу тест, он проходит.
                                                    Тестер тестирует фичу, она не срабатывает.
                                                    Я показываю ему тест, он его понимает и говорит, что то ли он не те данные вводил, то ли выполнял не те команды.
                                                    После того как мы с тестером согласны, что сценарий напиан правильно и у него он срабатывает, фича считается работающей и проверяется на каждой итерации тестирования.

                                                    Вот и всё что я хочу. Behat не нужен мне в плане методологии. Я ею не занимаюсь. А вот тестами и коммуникацией с тестировщиком и менеджером — постоянно.
                                    • 0
                                      Да, и кстати конкретно в этой хабровской заметке Behat подается как альтернатива функциональному тестированию. О похожей альтернативе я и говорю. Она не требует соблюдения всех формальностей описания Story.
                • 0
                  Я вообще имел в виду, что для написания функциональных тестов использовать xUnit не самое разумное решение и есть специализированные инструменты. Все остальное ниже за меня написали :)
            • 0
              Behat — альтернатива не функциональному тестированию, а альтернатива функциональному тестированию через PHPUnit. Ну и конечно же один из способов BDD разработки.
              • 0
                Лучше для функционального тестирования использовать специализированные инструменты, мне кажется, например, selenium или selenium rc. Хотя, функциональные тесты на, например, XML-API мы тоже пишем на xUnit.
                • 0
                  Вместо отдельного Selenium'а можно использовать Mink с SahiDriver'ом. Сам, к сожалению, пока не пробовал такого варианта, но выглядит проще и удобнее, чем Selenium.
              • 0
                Причем с помощью прокси можно тестить на разных операционых системах и в разных броузерах.

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