Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BDD в PHP вместе с Behat и Mink

BDD в PHP вместе с Behat и Mink

Презентация с ZFConf11 про Behavior Driven Development в PHP проектах с использованием библиотек Behat и Mink

Konstantin Kudryashov

September 26, 2011
Tweet

More Decks by Konstantin Kudryashov

Other Decks in Programming

Transcript

  1. Я кто такой senior from-birth web developer в International speaker

    Разработчик Behat, Mink Разработчик jade.php Разработчик capifony Core-contributor Symfony2 framework Разработчик плагинов symfony и Symfony2 [email protected] http://github.com/everzet http://card.everzet.com everzet @
  2. Test-Driven Development Мы на самом деле говорим о тестах??? Но

    каким образом тестировать то, чего еще нет?
  3. testFindsCustomerById() testFailsForDuplicateCustomers() Тест-кейсы должны начинаться со слова “should” shouldFindCustomerById() shouldFailForDuplicateCustomers()

    BDDбыл создан как набор конвенций поверх TDD Тест-кейсы должы составлять предложения
  4. shouldFindCustomerById() shouldFailForDuplicateCustomers() Класс тест-кейсов должен представлять из себя существительное для

    кейсов class CustomerTableTest extends \PHPUnitTestCase { /** * @Test */ shouldFindCustomerById() ... } BDDбыл создан как набор конвенций поверх TDD Тест-кейсы должны начинаться со слова “should” Тест-кейсы должы составлять предложения testFindsCustomerById() testFailsForDuplicateCustomers()
  5. assertEquals($expected, $actual) assertGreaterThan($expected, $actual) assertInstanceOf($class, $actual) $actual should be Equals

    to $expected $actual should be GreaterThan $expected $actual should be InstanceOf $class Описываем АССЕРШЕНЫ тоже TEST-ориентированы ТЕСТируем
  6. RSpec # bowling_spec.rb require '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 end end
  7. RSpec # bowling_spec.rb require '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 end end Пишем СПЕЦИФИКАЦИЮ, а не UnitTEST
  8. UnitTest TDD Spec BDD Сначала дизайн Dan North BDD История

    Тесты вперед Автоматизация тестов
  9. photo by dsearls photo by Horia Varlan для заказчиков СЛОВАРЬ

    для девелоперов для аналитиков для тестеров
  10. photo by dsearls photo by Horia Varlan 1 СЛОВАРЬ для

    заказчиков для девелоперов для аналитиков для тестеров
  11. photo by dsearls photo by Horia Varlan тестеры аналитики девелоперы

    заказчики ИСКОРЕНИТ множество проблем ДИЗАЙНА и КОММУНИКАЦИЙ 1 СЛОВАРЬ
  12. A ⎯ добавочное знач. (профит) функционала B ⎯ профитирующая персона

    (роль) C ⎯ функционал Чтобы [A] В качестве [B] Мне нужно [C] Наратив:
  13. ⎯ Сила данной конструкции в том, что она требует определения

    профита от функционала еще до его реализации © Dan North A ⎯ добавочное знач. (профит) функционала B ⎯ профитирующая персона (роль) C ⎯ функционал Чтобы [A] В качестве [B] Мне нужно [C] Наратив:
  14. Поведение story ⎯ это ее приемочный критерий! ⎯ если система

    удовлетворяет все приемочные критерии, то она работает верно; если не выполняет - неверно.
  15. Given some initial context (the givens), When an event occurs,

    Then ensure some outcomes. In order to ... As a ... I need ... Story:
  16. Given some initial context (the givens), When an event occurs,

    Then ensure some outcomes. Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Story: In order to ... As a ... I need ...
  17. Scenario 1: Scenario 2: Story: Given some initial context (the

    givens), When an event occurs, Then ensure some outcomes. Given some initial context (the givens), When an event occurs, Then ensure some outcomes. In order to ... As a ... I need ...
  18. UnitTest TDD Spec BDD Scenario BDD Сначала анализ Dan North

    BDD История Сначала дизайн Тесты вперед Автоматизация тестов
  19. Сначала анализ Сначала дизайн UnitTest TDD Spec BDD Scenario BDD

    Dan North BDD История Тесты вперед Автоматизация тестов +
  20. Given some initial context (the givens), When an event occurs,

    Then ensure some outcomes. In order to ... As a ... I need ... Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Scenario 1: Scenario 2: Story:
  21. Given some initial context (the givens), When an event occurs,

    Then ensure some outcomes. In order to ... As a ... I need ... Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Scenario: 1st scenario title Scenario: 2nd scenario title Feature: Feature description
  22. 1. feature 2. scenario 3. step ... ... 2. scenario

    3. step ... ... Given some initial context (the givens) When an event occurs Then ensure some outcomes In order to ... As a ... I need ... Given some initial context (the givens) When an event occurs Then ensure some outcomes Scenario: 1st scenario title Scenario: 2nd scenario title Feature: Feature description feature tree
  23. Given some initial context (the givens) When an event occurs

    Then ensure some outcomes In order to ... As a ... I need ... Given some initial context (the givens) When an event occurs Then ensure some outcomes Scenario: 1st scenario title Scenario: 2nd scenario title Feature: Feature description
  24. Etant donné some initial context (the givens) Lorsque an event

    occurs Alors ensure some outcomes In order to ... As a ... I need ... Etant donné some initial context (the givens) Lorsque an event occurs Alors ensure some outcomes Scénario: 1st scenario title Scénario: 2nd scenario title Fonctionnalité: Feature description # language: fr
  25. ͳΒ͹ some initial context (the givens) ͔͠͠ an event occurs

    લఏ ensure some outcomes In order to ... As a ... I need ... ͳΒ͹ some initial context (the givens) ͔͠͠ an event occurs લఏ ensure some outcomes γφϦΦ: 1st scenario title γφϦΦ: 2nd scenario title ϑΟʔνϟ: Feature description # language: ja
  26. Допустим some initial context (the givens) Когда an event occurs

    То ensure some outcomes In order to ... As a ... I need ... Допустим some initial context (the givens) Когда an event occurs То ensure some outcomes Сценарий: 1st scenario title Сценарий: 2nd scenario title Функционал: Feature description # language: ru
  27. Let go and haul some initial context (the givens) Blimey!

    an event occurs Aye ensure some outcomes In order to ... As a ... I need ... Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: 1st scenario title Heave to: 2nd scenario title Ahoy matey!: Feature description # language: en-pirate
  28. Let go and haul some initial context (the givens) Blimey!

    an event occurs Aye ensure some outcomes Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: Heave to: Ahoy matey!: # language: en-pirate
  29. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим:

    $ pear install behat/behat 3. Инициализируем: $ cd path/to/project && behat --init
  30. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим:

    $ pear install behat/behat 3. Инициализируем: $ cd path/to/project && behat --init +d features - place your *.feature files here +d features/steps - place step definition files here +f features/steps/steps.php - place some step definitions in this file +d features/support - place support scripts and static files here +f features/support/bootstrap.php - place bootstrap scripts in this file +f features/support/env.php - place environment initialization scripts in this file
  31. # language: ru Функционал: Утилита ls Чтобы узнать содержимое директории

    Как пользователь UNIX Я должен иметь утилиту листинга директорий
  32. Сценарий: 2 файла в директории Чтобы узнать содержимое директории Как

    пользователь UNIX Я должен иметь утилиту листинга директорий # language: ru Функционал: Утилита ls
  33. Допустим я нахожусь в директории “test1” Если я исполню “ls”

    То я должен увидеть: Сценарий: 2 файла в директории Чтобы узнать содержимое директории Как пользователь UNIX Я должен иметь утилиту листинга директорий # language: ru Функционал: Утилита ls """ file_one.txt file_foo.txt """
  34. 1. feature 2. scenario Допустим я нахожусь в директории “test1”

    Если я исполню “ls” То я должен увидеть: Сценарий: 2 файла в директории Чтобы узнать содержимое директории Как пользователь UNIX Я должен иметь утилиту листинга директорий # language: ru Функционал: Утилита ls """ file_one.txt file_foo.txt """
  35. Допустим я нахожусь в директории “test1” Если я исполню “ls”

    То я должен увидеть: Сценарий: 2 файла в директории Чтобы узнать содержимое директории Как пользователь UNIX Я должен иметь утилиту листинга директорий # language: ru Функционал: Утилита ls """ file_one.txt file_foo.txt """
  36. <?php Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw new

    \Behat\Behat\Exception\Pending(); } ); ОПРЕДЕЛЕНИЯШАГОВ Допустим я нахожусь в директории “test1”
  37. <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw new

    \Behat\Behat\Exception\Pending(); } ); ОПРЕДЕЛЕНИЯШАГОВ Допустим я нахожусь в директории “test1”
  38. <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw new

    \Behat\Behat\Exception\Pending(); } ); ??? ОПРЕДЕЛЕНИЯШАГОВ Допустим я нахожусь в директории “test1”
  39. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

    шаг ⎯ у которого нет (не найдено) определений ТИПЫРЕЗУЛЬТАТОВШАГОВ
  40. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

    шаг ⎯ у которого нет (не найдено) определений 3. Ambiguous шаг ⎯ который подпадает под несколько определений ТИПЫРЕЗУЛЬТАТОВШАГОВ
  41. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

    шаг ⎯ у которого нет (не найдено) определений 3. Ambiguous шаг ⎯ который подпадает под несколько определений 4. Failed шаг ⎯ который throw \Exception(); ТИПЫРЕЗУЛЬТАТОВШАГОВ
  42. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

    шаг ⎯ у которого нет (не найдено) определений 3. Ambiguous шаг ⎯ который подпадает под несколько определений 4. Failed шаг ⎯ который throw \Exception(); 5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии ТИПЫРЕЗУЛЬТАТОВШАГОВ
  43. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

    шаг ⎯ у которого нет (не найдено) определений 3. Ambiguous шаг ⎯ который подпадает под несколько определений 4. Failed шаг ⎯ который throw \Exception(); 5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии 6. Passed шаг ⎯ который не кидает эксепшенов ТИПЫРЕЗУЛЬТАТОВШАГОВ
  44. ОПРЕДЕЛЕНИЯШАГОВ <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw

    new \Behat\Behat\Exception\Pending(); } ); Допустим я нахожусь в директории “test1”
  45. ОПРЕДЕЛЕНИЯШАГОВ Если я исполню “ls” <?php $steps->Допустим('/^я нахожусь в директории

    “(.*)”$/', function() { throw new \Behat\Behat\Exception\Pending(); } ); Допустим я нахожусь в директории “test1”
  46. <?php $steps->Если('/^я исполню “(.*)”$/', function($dollars) { throw new \Behat\Behat\Exception\Pending(); }

    ); ОПРЕДЕЛЕНИЯШАГОВ Если я исполню “ls” <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw new \Behat\Behat\Exception\Pending(); } ); Допустим я нахожусь в директории “test1”
  47. <?php $steps->Если('/^я исполню “(.*)”$/', function($command) { // $command === “ls”

    } ); ОПРЕДЕЛЕНИЯШАГОВ Если я исполню “ls” <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function($dir) { // $dir === “test1” } ); Допустим я нахожусь в директории “test1”
  48. <?php $steps->Если('/^я исполню “(.*)”$/', function($command) { exec($command, $output); $output =

    trim(implode(“\n”, $output)); } ); ОПРЕДЕЛЕНИЯШАГОВ Если я исполню “ls” <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function($dir) { chdir('fixtures/' . $dir); } ); Допустим я нахожусь в директории “test1”
  49. <?php $steps->Если('/^я исполню “(.*)”$/', function($world, $command) { exec($command, $output); $world->output

    = trim(implode(“\n”, $output)); } ); ОПРЕДЕЛЕНИЯШАГОВ Если я исполню “ls” <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function($world, $dir) { chdir('fixtures/' . $dir); } ); Допустим я нахожусь в директории “test1”
  50. <?php $steps->То('/^я должен увидеть:$/', function($world, $string) { if ($world->output !==

    (string) $string) { throw new \Exception('Неверный вывод'); } } ); ПРОВЕРЯЕМРЕЗУЛЬТАТЫ То я должен увидеть:
  51. <?php $steps->То('/^я должен увидеть:$/', function($world, $string) { assertEquals((string) $string, $world->output);

    } ); using PHPUnit <?php $steps->То('/^я должен увидеть:$/', function($world, $string) { if ($world->output !== (string) $string) { throw new \Exception('Неверный вывод'); } } ); ПРОВЕРЯЕМРЕЗУЛЬТАТЫ То я должен увидеть: То я должен увидеть: ( )
  52. <?php $steps->Допустим('/^я нахожусь в директории “(.*)”$/', function($world, $dir) { chdir('fixtures/'

    . $dir); } ); $steps->Если('/^я исполню “(.*)”$/', function($world, $command) { exec($command, $output); $world->output = trim(implode(“\n”, $output)); } ); $steps->То('/^я должен увидеть:$/', function($world, $string) { assertEquals((string) $string, $world->output); } ); ОПРЕДЕЛЕНИЯШАГОВ
  53. <?php $steps-> Допустим('/^я нахожусь в директории “(.*)”$/', function($world, $dir) {

    chdir('fixtures/' . $dir); } )-> Если('/^я исполню “(.*)”$/', function($world, $command) { exec($command, $output); $world->output = trim(implode(“\n”, $output)); } )-> То('/^я должен увидеть:$/', function($world, $string) { assertEquals((string) $string, $world->output); } ) ; ОПРЕДЕЛЕНИЯШАГОВ
  54. $ behat features/ 1. Описываем поведение 2. Проверяем поведение (

    ) 4. Проверяем поведение 3. Реализуем поведение Workflow
  55. $ behat features/ 1. Описываем поведение 2. Проверяем поведение (

    ) 4. Проверяем поведение 3. Реализуем поведение Workflow
  56. 3.1. Пишем спеки $ behat features/ 1. Описываем поведение 2.

    Проверяем поведение ( ) 3.4. Прогоняем спеки 3.3. Пишем код 3.2. Прогоняем спеки 4. Проверяем поведение 3. Реализуем поведение: Workflow
  57. 3.1. Пишем спеки $ behat features/ 1. Описываем поведение 3.4.

    Прогоняем спеки 3.3. Пишем код 3.2. Прогоняем спеки 3. Реализуем поведение: 2. Проверяем поведение ( ) 4. Проверяем поведение Workflow
  58. <?php use Behat\Mink\Mink, Behat\Mink\Session, Behat\Mink\Driver\GoutteDriver, Behat\Mink\Driver\SahiDriver; // инициализируем Mink и

    сессии $mink = new Mink(); $mink->registerSession('goutte', new Session(new GoutteDriver($startUrl)) ); $mink->registerSession('javascript',, new Session(new SahiDriver($startUrl, 'firefox')) ); // выполняем действия в стандартном драйвере $mink->getSession('goutte')->getPage()->clickLink('Downloads'); echo $mink->getSession('goutte')->getPage()->getContent(); // выполняем действия в javascript (Sahi) сессии $mink->getSession('javascript')->getPage()->clickLink('Downloads'); echo $mink->getSession('javascript')->getPage()->getContent();
  59. Новый проект 1. Создаем каркас проекта: $ cd path/to/project &&

    zf ... Getting Started with Zend Framework By Rob Allen, www.akrabat.com Document Revision 1.7.6 Copyright © 2006, 2010
  60. Новый проект 1. Создаем каркас проекта: $ cd path/to/project &&

    zf ... 2. Инициализируем B$%&': $ behat --init
  61. Новый проект 3. Знакомим B$%&' с M!"#: $ vim behat.yml

    # behat.yml default: environment: parameters: start_url: http://tutorial.zf.dev/ imports: - mink/behat.yml $ vim features/support/bootstrap.php <?php // features/support/bootstrap.php require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Framework/Assert/Functions.php'; require_once 'mink/autoload.php'; $ behat --steps --lang ru
  62. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

    управлять коллекцией альбомов Сценарий: Добавление альбома Допустим я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Cilico" И я должен видеть "Edit" # language: ru Функционал: Альбомы
  63. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

    управлять коллекцией альбомов Сценарий: Добавление альбома Допустим я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Cilico" И я должен видеть "Edit" # language: ru Функционал: Альбомы
  64. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

    управлять коллекцией альбомов Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit" # language: ru Функционал: Альбомы
  65. <?php # features/support/bootstrap.php // Конфигурация и инициализация тестовой среды ZF

    <?php # features/steps/steps.php $steps->Допустим('/^в базе нет альбомов$/', function($world) { $albums = new Application_Model_DbTable_Albums(); $albums->delete(1); } );
  66. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

    управлять коллекцией альбомов Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit" # language: ru Функционал: Альбомы
  67. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

    управлять коллекцией альбомов @javascript Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit" # language: ru Функционал: Альбомы