Различные CMS

Laravel, BDD и вы: начинаем работу

Добро пожаловать в эту серию статей о разработке приложений Laravel с использованием подхода, основанного на поведении (BDD). Полный стек BDD может казаться сложным и пугающим. Сколько есть разных разработчиков, столько же существует различных способов сделать это.

В этой серии я расскажу вам о том, как использовать Behat и PhpSpec для разработки приложения Laravel с нуля.

По BDD есть много ресурсов, но конкретных материалов по Laravel трудно найти. Поэтому в этой серии мы сосредоточимся больше на аспектах, связанных с Laravel, и меньше на общем материале, который вы можете прочитать в многих других местах.

Описание поведения

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

Когда мы это сделаем, что мы ожидаем получить?

Нам не разрешается писать какой-либо код до тех пор, пока у нас не будет неудачного, красного шага, или пока если мы не реорганизуем существующий код.

Иногда необходимо будет итеративно решить небольшую часть истории или функции (два слова, которые я использую взаимозаменяемо), над которыми мы работаем. Это часто означает, что нужно писать спецификации с помощью PhpSpec. Иногда на интеграционном уровне или уровне модуля потребуется много итераций до того, как пройдет вся история (на уровне приема). Все это звучит очень сложно, но на самом деле это не так. Я очень верю в то, что вы научитесь, поэтому я думаю, что все станет более ясно, если мы начнем писать какой-то фактический код.

Мы будем писать истории и специфиации на четырех разных уровнях:

1. Приемочный

В большинстве случаев наш функциональный набор будет служить нашим приемочным уровнем. То, как мы будем описывать наши функции в нашем функциональном наборе, будет очень похоже на то, как мы будем писать истории принятия (используя Фреймворк с браузером) и, таким образом, создадим много дублирования.

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

2. Функциональные

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

3. Интеграция

Наш набор интеграции проверяет поведение основной части нашего приложения, которая не обязательно должна иметь доступ к Laravel. Набор интеграции обычно представляет собой смесь историй Behat и спецификаций PhpSpec.

4. Unit

Наши модульные тесты будут написаны на PhpSpec и будут тестировать изолированные небольшие единицы ядра приложения. Все наши сущности, объекты-значения и т.д. будут иметь спецификации.

Пример использования 

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

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

  1. функциональный
  2. Интеграционный
  3. Модульный

В нашем функциональном наборе тестов мы будем обходить HTTP-ответы нашего приложения в режиме headless, что означает, что мы не будем открывать браузер. Это упростит взаимодействие с Laravel.

Позже, если мы закончим с более сложным интерфейсом и, возможно, придется протестировать и некоторый JavaScript, мы можем добавить специальный набор для приемочных тестов. Эта серия статей все еще в процессе создания, поэтому не стесняйтесь оставлять свои предложения в разделе комментариев.

Наши настройки

Обратите внимание, что для этого урока я предполагаю, что у вас установлена новая версия Laravel (4.2). Вы также используете Laravel Homestead, и это то, что я использовал, когда писал этот код.

Прежде чем мы приступим к реальной работе, давайте убедимся, что у нас есть Behat и PhpSpec. Во-первых, хотя мне нравится делать чистку всякий раз, когда я начинаю новый проект laravel и удаляю то, что мне не нужно:

git rm -r app/tests/ phpunit.xml CONTRIBUTING.md

Если вы удалите эти файлы, обязательно обновите файл composer.json соответственно:

«autoload»: {
«classmap»: [
«app/commands»,
«app/controllers»,
«app/models»,
«app/database/migrations»,
«app/database/seeds»
]
},

И, конечно же:

$ composer dump-autoload

Теперь мы готовы задействовать инструменты BDD, которые нам нужны. Просто добавьте раздел require-dev к вашему composer.json:

«require»: {
«laravel/framework»: «4.2.*»
},
«require-dev»: {
«behat/behat»: «~3.0»,
«phpspec/phpspec»: «~2.0»,
«phpunit/phpunit»: «~4.1»
},

Вы можете подумать: «Почему мы пытаемся использовать PHPUnit?»? Мы не собираемся писать старые добрые тестовые примеры PHPUnit в этой серии, но их утверждения — удобный инструмент вместе с Behat. Мы увидим это позже в этой статье, когда мы напишем нашу первую функцию.

Не забудьте обновить свои зависимости после изменения composer.json:

$ composer update —dev

Мы почти закончили установку и настройку. PhpSpec работает из коробки:

$ vendor/bin/phpspec run

0 specs
0 examples
0ms

Но для Behat нужно сделать быстрый запуск с параметром --init, чтобы все настроить:

$ vendor/bin/behat —init

+d features — place your *.feature files here
+d features/bootstrap — place your context classes here
+f features/bootstrap/FeatureContext.php — place your definitions, transformations and hooks here

$ vendor/bin/behat

No scenarios
No steps
0m0.14s (12.18Mb)

Первая команда создала новый класс FeatureContext, где мы можем написать определения шагов, необходимые для наших функций:

<?php

use BehatBehatContextSnippetAcceptingContext;
use BehatGherkinNodePyStringNode;
use BehatGherkinNodeTableNode;

/**
* Behat context class.
*/
class FeatureContext implements SnippetAcceptingContext
{
/**
* Initializes context.
*
* Every scenario gets its own context object.
* You can also pass arbitrary arguments to the context constructor through behat.yml.
*/
public function __construct()
{
}
}

Написание нашей первой функции

Наша первая функция будет очень простой: мы просто убедимся, что наша новая установка Laravel приветствует нас на главной странице. Я добавил довольно глупый Given шаг вместе с Given I am logged in,  что показывает, насколько легко взаимодействовать с Laravel в наших функциях.

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

Идем дальше и создаем файл welcome.feature и помещаем его в features/functional:

# features/functional/welcome.feature

Feature: Welcoming developer
As a Laravel developer
In order to proberly begin a new project
I need to be greeted upon arrival

Scenario: Greeting developer on homepage
Given I am logged in
When I visit «/»
Then I should see «You have arrived.»

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

Мне нравится держать вещи красивыми и чистыми, поэтому я считаю, что у нас должен быть специальный контекст функции для нашего функционального пакета, который может дать нам доступ к Laravel. Вы можете просто скопировать существующий файл FeatureContext и изменить имя класса на LaravelFeatureContext. Для этого нам также нужен файл конфигурации behat.yml.

Создайте его в корневом каталоге проекта и добавьте следующее:

default:
suites:
functional:
paths: [ %paths.base%/features/functional ]
contexts: [ LaravelFeatureContext ]

Я думаю, что YAML здесь очень понятен. Наш функциональный пакет будет искать функции в каталоге functional и запускать их через LaravelFeatureContext.

Если мы попытаемся запустить Behat в этот момент, он укажет нам реализовать необходимые определения шагов. Мы можем заставить Behat добавить пустые методы заглушки в LaravelFeatureContext с помощью следующей команды:

$ vendor/bin/behat —dry-run —append-snippets
$ vendor/bin/behat

Feature: Welcoming developer
As a Laravel developer
In order to proberly begin a new project
I need to be greeted upon arival

Scenario: Greeting developer on homepage # features/functional/welcome.feature:6
Given I am logged in # LaravelFeatureContext::iAmLoggedIn()
TODO: write pending definition
When I visit «/» # LaravelFeatureContext::iVisit()
Then I should see «You have arrived.» # LaravelFeatureContext::iShouldSee()

1 scenario (1 pending)
3 steps (1 pending, 2 skipped)
0m0.28s (12.53Mb)

И теперь, как вы можете видеть из вывода, мы готовы начать выполнение первого из наших шагов: Given I am logged in.

Тестовый кейс PHPUnit, поставляемый с Laravel, позволяет нам делать такие вещи, как $this->be($ user), что позволяет залогинить определенного пользователя. В конечном счете, мы хотим иметь возможность взаимодействовать с Laravel, как если бы мы использовали PHPUnit, поэтому давайте продолжим и напишем код определения шага «we wish we had»:

/**
* @Given I am logged in
*/
public function iAmLoggedIn()
{
$user = new User;

$this->be($user);
}

Конечно, это не сработает, так как Behat понятия не имеет о том, что касается Laravel, но я покажу вам, как легко заставить Behat и Laravel работать вместе.

Если вы посмотрите в исходном коде Laravel и найдите класс IlluminateFoundationTestingTestCase, который является классом, из которого наследуется тестовый пример по умолчанию, вы увидите, что, начиная с Laravel 4.2, все перемещено в трейт. ApplicationTrait теперь отвечает за загрузку экземпляра Application, настройку HTTP-клиента и предоставление нам нескольких вспомогательных методов, таких как be().

Это довольно круто, главным образом потому, что это означает, что мы можем просто добавить этот трейт в наши контексты Behat. У нас также есть доступ к AssertionsTrait, но он все еще привязан к PHPUnit.

Когда мы добавляем трейт, нам нужно сделать две вещи. Нам нужно иметь метод setUp(), как тот, который находится в классе IlluminateFoundationTestingTestCase, и нам нужен метод createApplication(), как тот, который используется в тестовом кейсе Laravel по умолчанию. Фактически мы можем просто скопировать эти два метода и использовать их напрямую.

Следует заметить только одно: в PHPUnit метод setUp() будет автоматически вызываться перед каждым тестом. Чтобы достичь этого в Behat, мы можем использовать аннотацию @BeforeScenario.

Добавьте в свой LaravelFeatureContext следующее:

use IlluminateFoundationTestingApplicationTrait;

/**
* Behat context class.
*/
class LaravelFeatureContext implements SnippetAcceptingContext
{
/**
* Responsible for providing a Laravel app instance.
*/
use ApplicationTrait;

/**
* Initializes context.
*
* Every scenario gets its own context object.
* You can also pass arbitrary arguments to the context constructor through behat.yml.
*/
public function __construct()
{
}

/**
* @BeforeScenario
*/
public function setUp()
{
if ( ! $this->app)
{
$this->refreshApplication();
}
}

/**
* Creates the application.
*
* @return SymfonyComponentHttpKernelHttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;

$testEnvironment = ‘testing’;

return require __DIR__.’/../../bootstrap/start.php’;
}

Довольно легко, и посмотрим, что получится, когда мы запустим Behat:

$ vendor/bin/behat

Feature: Welcoming developer
As a Laravel developer
In order to proberly begin a new project
I need to be greeted upon arival

Scenario: Greeting developer on homepage # features/functional/welcome.feature:6
Given I am logged in # LaravelFeatureContext::iAmLoggedIn()
When I visit «/» # LaravelFeatureContext::iVisit()
TODO: write pending definition
Then I should see «You have arrived.» # LaravelFeatureContext::iShouldSee()

1 scenario (1 pending)
3 steps (1 passed, 1 pending, 1 skipped)
0m0.73s (17.92Mb)

Зеленый первый шаг, что означает, что наша установка работает!

Затем мы можем реализовать шаг When I visit. Это очень просто, и мы можем просто использовать метод call(), который предоставляет ApplicationTrait. Одна строка кода доставит нас туда:

/**
* @When I visit :uri
*/
public function iVisit($uri)
{
$this->call(‘GET’, $uri);
}

Последний шаг: Then I should see, занимает немного больше, и нам нужно задействовать две зависимости. Для утверждения нам понадобится PHPUnit, и нам понадобится Symfony DomCrawler для поиска текста «You have arrived».

Мы можем реализовать это так:

use PHPUnit_Framework_Assert as PHPUnit;
use SymfonyComponentDomCrawlerCrawler;

/**
* @Then I should see :text
*/
public function iShouldSee($text)
{
$crawler = new Crawler($this->client->getResponse()->getContent());

PHPUnit::assertCount(1, $crawler->filterXpath(«//text()[. = ‘{$text}’]»));
}

Это почти тот же код, как если бы вы использовали PHPUnit. Часть filterXpath() немного запутанна, и мы не будем беспокоиться об этом сейчас, так как она немного выходит за рамки этой статьи. Просто доверьтесь мне, что это работает.

И наконец мы запускаем Behat — хорошая новость:

$ vendor/bin/behat
Feature: Welcoming developer
As a Laravel developer
In order to proberly begin a new project
I need to be greeted upon arival

Scenario: Greeting developer on homepage # features/functional/welcome.feature:6
Given I am logged in # LaravelFeatureContext::iAmLoggedIn()
When I visit «/» # LaravelFeatureContext::iVisit()
Then I should see «You have arrived.» # LaravelFeatureContext::iShouldSee()

1 scenario (1 passed)
3 steps (3 passed)
0m0.82s (19.46Mb)

Функция работает так, как ожидалось, и мы видим приветствие.

Вывод

Теперь полный LaravelFeatureContext должен выглядеть примерно так:

<?php

use BehatBehatContextSnippetAcceptingContext;
use BehatGherkinNodePyStringNode;
use BehatGherkinNodeTableNode;

use PHPUnit_Framework_Assert as PHPUnit;
use SymfonyComponentDomCrawlerCrawler;

use IlluminateFoundationTestingApplicationTrait;

/**
* Behat context class.
*/
class LaravelFeatureContext implements SnippetAcceptingContext
{
/**
* Responsible for providing a Laravel app instance.
*/
use ApplicationTrait;

/**
* Initializes context.
*
* Every scenario gets its own context object.
* You can also pass arbitrary arguments to the context constructor through behat.yml.
*/
public function __construct()
{
}

/**
* @BeforeScenario
*/
public function setUp()
{
if ( ! $this->app)
{
$this->refreshApplication();
}
}

/**
* Creates the application.
*
* @return SymfonyComponentHttpKernelHttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;

$testEnvironment = ‘testing’;

return require __DIR__.’/../../bootstrap/start.php’;
}

/**
* @Given I am logged in
*/
public function iAmLoggedIn()
{
$user = new User;

$this->be($user);
}

/**
* @When I visit :uri
*/
public function iVisit($uri)
{
$this->call(‘GET’, $uri);
}

/**
* @Then I should see :text
*/
public function iShouldSee($text)
{
$crawler = new Crawler($this->client->getResponse()->getContent());

PHPUnit::assertCount(1, $crawler->filterXpath(«//text()[. = ‘{$text}’]»));
}
}

Теперь у нас есть действительно хорошая основа для развития, поскольку мы продолжаем разработку нашего нового приложения Laravel с использованием BDD. Надеюсь, я доказал вам, как легко заставить Laravel и Behat вместе работать. 

В этой первой статье мы затронули множество разных тем. Не нужно волноваться, мы будем более подробно рассматривать их, поскольку эта серия статей продолжается. Если у вас есть какие-либо вопросы или предложения, оставьте свой комментарий.

Источник: code.tutsplus.com

Похожие записи

Загрузка ....