bdd для php проектов
TRANSCRIPT
BEHAVIORDRIVENDEVELOPMENTДЛЯ PHP ПРОЕКТОВ
@everzet
КТО Я
ПРОГРАММИСТС РОЖДЕНИЯ
DEV.BYВЕДУЩИЙ РАЗРАБОТЧИК
symfony-разработчикС 2007 годаСОЗДАТЕЛЬBEHAT
http://everzet.com
РАЗРАБОТКАINSIDE-OUT
Тесты двигателя
Тесты электрики
Тесты колес
Тесты соединений
ТЕСТЫUNIT
TDD
=качественныйпродукт?+
разработкаunit-тесты
=+разработкаunit-тесты
качественный-продукт
TDD
ОТДЕЛЬНЫХ МОДУЛЕЙНЕ ГАРАНТИРУЮТ КАЧЕСТВОРАБОТЫ СИСТЕМЫ В ЦЕЛОМ
UNIT-ТЕСТЫ
РАЗРАБОТКАOUTSIDE-IN
ВХОДНЫЕ/ВЫХОДНЫЕ
ДАННЫЕ
входныевыходные
данныеданные
RSpec
# bowling_spec.rbrequire 'bowling'
describe Bowling, "#score" do it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should == 0 endend
<?phprequire_once 'bowling.php'
class DescribeIndexController extends PHPSpec_Context_Zend{ public function itShouldDisplayHelloWorld() { $this->get(‘index’); $this-> response()-> should-> match(‘/Hello World/’); }}
PHPSpec
<?php
$browser->get(‘/job/new’)-> with(‘request’)->begin()-> isParameter(‘module’, ‘job’)-> isParameter(‘action’, ‘new’)-> end()->
click(‘Preview your job’, array(‘job’ => array( ‘company’ => ‘Sensio Labs’, ‘url‘ => ‘http://sensio.com’, ‘logo‘ => ‘/uploads/jobs/sensio-labs.gif’, ));
Symfony Functional Test
BDD
) =качественныйпродукт
+ (
разработка
unit-тесты
+ПОВЕДЕНИЕ
ТЕХНИКАКОММУНИКАЦИЙ
ИДЕЯ
# bowling_spec.rbrequire 'bowling'
describe Bowling, "#score" do it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should == 0 endend
ИДЕЯ
# bowling_spec.rbrequire 'bowling'
describe Bowling, "#score" do it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should == 0 endend
NO!
ИДЕЯХочу онлайн-боулинг
с блэкджеком и...
и он будетПЛАТНЫМ
КОММУНИКАЦИИ
Хочу кастомный Porsche
написание спек
разработка
идея
# bowling_spec.rbrequire 'bowling'
describe Bowling, "#score" do it "returns 0 for all gutter game" do bowling = Bowling.new 20.times { bowling.hit(0) } bowling.score.should == 0 endend
ФУНКЦИОНАЛ
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers
Scenario: Add two numbers Given I have entered 10 into the calculator And I have entered 5 into the calculator When I press ‘plus’ Then the result should be 15
ФУНКЦИОНАЛ
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers
Scenario: Add two numbers Given I have entered 10 into the calculator And I have entered 5 into the calculator When I press ‘plus’ Then the result should be 15
СЦЕНАРИИ
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers
Scenario: Add two numbers Given I have entered 10 into the calculator And I have entered 5 into the calculator When I press ‘plus’ Then the result should be 15
ШАГИ
Feature: Addition In order to avoid silly mistakes As a math idiot I want to be told the sum of two numbers
Scenario: Add two numbers Given I have entered 10 into the calculator And I have entered 5 into the calculator When I press ‘plus’ Then the result should be 15
БИБЛИОТЕКИ
ASSERTIONS
RUNNER
BROWSER
Test::Unit, RSpec
Webrat, Capybara, Selenium
Autotest, RStakeout, Watchr
require 'calculator'
Given /I have entered (\d+) into the calculator/ do|n| @calc.push n.to_iend
When /I press (\w+)/ do |op| @result = @calc.send opend
Then /the resoulr should be (.*)/ do |result| @result.should == result.to_fend
ОПРЕДЕЛЕНИЯ
require 'calculator'
Given /I have entered (\d+) into the calculator/ do|n| @calc.push n.to_iend
When /I press (\w+)/ do |op| @result = @calc.send opend
Then /the resoulr should be (.*)/ do |result| @result.should == result.to_fend
ОПРЕДЕЛЕНИЯ
RubyПИШУТСЯ на
МИНУСЫ
1. Не все PHP-разработчики знают/хотят знать Ruby2. Сложность при описании входных условий
4. Скорость работы кросс-языкового решения3. Невозможность использования PHP библиотек
1. Написан с нуля на PHP5.32. Написан с применением Symfony Components
4. Полностью нативное решение на PHP3. Старается быть Cucumber’ом с входными/выходными данными
6. Поддерживает полную и17ю7. Полностью расширяем и настраиваем8. Поддерживает различные типы лоадеров
5. Столь же быстр, что и Cucumber (проверено)
Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times
Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ When I press the coffee button Then I should be served a coffee
ФУНКЦИОНАЛ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { if (‘fail’ === $success) { assertNotEquals(0, $world->return); } else { assertEquals(0, $world->return); } });
ОПРЕДЕЛЕНИЯ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { if (‘fail’ === $success) { assertNotEquals(0, $world->return); } else { assertEquals(0, $world->return); } });
ОПРЕДЕЛЕНИЯ
ТИП ОПРЕДЕЛЕНИЯ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { if (‘fail’ === $success) { assertNotEquals(0, $world->return); } else { assertEquals(0, $world->return); } });
ОПРЕДЕЛЕНИЯ
РЕГУЛЯРНОЕ ВЫРАЖЕНИЕ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { if (‘fail’ === $success) { assertNotEquals(0, $world->return); } else { assertEquals(0, $world->return); } });
CALLBACK
ОПРЕДЕЛЕНИЯ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { ... });
Feature: Behat Console Runner Scenario: Run feature from CLI Given I have default Behat configuration When I call ‘behat -f progress’ Then it should pass
ИСПОЛНЕНИЕ
<?php
$steps->Then(‘/^it should (fail|pass)$/’, function($world, $status) { // $status == ‘pass’ });
Feature: Behat Console Runner Scenario: Run feature from CLI Given I have default Behat configuration When I call ‘behat -f progress’ Then it should pass
ИСПОЛНЕНИЕ
<?php
$steps->Then(‘/^it should pass$/’, function($world) { return true;});
$steps->Then(‘/^it passes$/’, function($world) { return false;});
$steps->Then(‘/^it should fail$/’, function($world) { throw new Exception();});
$steps->Then(‘/^it should pend$/’, function($world) { throw new Everzet\Behat\Exception\Pending();});
СТАТУСЫ
<?php
$steps->Then(‘/^it should pass$/’, function($world) { ...});
$steps->Then(‘/^it should pass$/’, function($world) { // Will throw exception on definitions read});
$steps->Then(‘/^it’s good$/’, function($world) { ...});
$steps->Then(‘/^it’s \w+$/’, function($world) { // Will throw exception on ‘it should pass call’});
СТАТУСЫ
ПРИМЕРИСПОЛЬЗОВАНИЯ
УСТАНОВКА
$> pear channel-discover pear.everzet.com$> pear install everzet/behat-beta
<?php # ./user.php
class User{ public function __construct($username, $age = 1) { }
public function getName() {}
public function getAge() {}}
HOW MUCH IS THE FISH?
# ./features/user.feature# language: ru
Функционал: Базовый Пользователь Чтобы работать с пользователями Как разработчик сайта Я хочу иметь доступ к пользовательской модели
Сценарий: Создание пользователя Допустим у нас нет пользователей Если мы добавим пользователя ‘everzet’ То у нас должно быть 1 пользователей И имя у первого пользователя ‘everzet’
FEATURE
GET SNIPPETS
<?php # ./features/steps/user_steps.php
require_once ‘PHPUnit/Autoload.php’;require_once ‘PHPUnit/Framework/Assert/Functions.php’;require_once __DIR__ . ‘/../../user.php’;
$steps->
Допустим(‘/^у нас нет пользователей$/’, function($world) { $world->users = array();})->
Если(‘/^мы добавим пользователя \’([^\’]+)\’$/’, function($world, $username) { $world->users[] = new User($username);})->
To(‘/^у нас должно быть (\d+) пользователей$/’, function($world, $count) { assertEquals($count, count($world->users));})->
То(‘/^имя у первого пользователя \’([^\’]+)\’$/’, function($world, $username) { assertEquals($username, $world->users[0]->getName());});
ОПРЕДЕЛЕНИЯ
WATCH IT FAILS
РЕАЛИЗАЦИЯ<?php # ./user.php
class User{ protected $name, $age;
public function __construct($username, $age = 1) { $this->name = $username; $this->age = $age; }
public function getName() { return $this->name; }
public function getAge() { return $this->age; }}
WATCH IT PASSES
1. CommonWebSteps2. Annotated class step definitions and hooks
4. PDF/HTML formatter3. JUnit formatter
5. Selenium integration
TODO
ССЫЛКИ
ОФФИЦИАЛЬНЫЙ САЙТhttp://everzet.com/Behat
ОФФИЦИАЛЬНЫЙ РЕПОЗИТОРИЙhttp://github.com/everzet/Behat
ОФФИЦИАЛЬНАЯ GOOGLE GROUPhttp://groups.google.com/group/behat
ВОПРОСЫ?
http://www.flickr.com/photos/jasmic/279741827http://www.flickr.com/photos/mulmatsherm/2312688473http://www.flickr.com/photos/joshfassbind/4584323789http://www.flickr.com/photos/bearuk/564788081http://www.flickr.com/photos/jasmincormier/3511034253http://www.flickr.com/photos/chuqui/4552338602
CREDITS