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. BDDв php

  2. Я кто такой everzet senior from-birth web developer в @

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

    Разработчик Behat, Mink Разработчик jade.php Разработчик capifony Core-contributor Symfony2 framework Разработчик плагинов symfony и Symfony2 everzet@knplabs.com http://github.com/everzet http://card.everzet.com everzet @
  4. BDD, Symfony2 эксперты Активные контрибуторы в open-source проекты Консультанты, аудиторы,

    тренеры http://knplabs.com
  5. История тестирования

  6. UnitTest История Автоматизация тестов

  7. UnitTest TDD Тесты вперед История Автоматизация тестов

  8. UnitTest TDD Dan North BDD История Тесты вперед Автоматизация тестов

  9. BDD ⎯ эволюция TDD

  10. - Эволюция-хренолюция... Что не так с TDD?

  11. Test-Driven Development

  12. Test-Driven Development Мы на самом деле говорим о тестах??? Но

    каким образом тестировать то, чего еще нет?
  13. Test-Driven Development дизайне На самом деле, мы говорим о

  14. Test-Driven Development Behavior © 2003, Dan North

  15. BDDбыл создан как набор конвенций поверх TDD

  16. Тест-кейсы должы составлять предложения BDDбыл создан как набор конвенций поверх

    TDD testFindsCustomerById() testFailsForDuplicateCustomers()
  17. testFindsCustomerById() testFailsForDuplicateCustomers() Тест-кейсы должны начинаться со слова “should” shouldFindCustomerById() shouldFailForDuplicateCustomers()

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

    кейсов class CustomerTableTest extends \PHPUnitTestCase { /** * @Test */ shouldFindCustomerById() ... } BDDбыл создан как набор конвенций поверх TDD Тест-кейсы должны начинаться со слова “should” Тест-кейсы должы составлять предложения testFindsCustomerById() testFailsForDuplicateCustomers()
  19. АССЕРШЕНЫ тоже TEST-ориентированы

  20. assertEquals($expected, $actual) assertGreaterThan($expected, $actual) assertInstanceOf($class, $actual) АССЕРШЕНЫ тоже TEST-ориентированы ТЕСТируем

  21. 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-ориентированы ТЕСТируем
  22. Specификационные BDD Фрэймворки

  23. *Spec RSpec by Dave Astels

  24. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk

  25. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk Fabulous

    by Alex Rudakov
  26. 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
  27. 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
  28. UnitTest TDD Spec BDD Сначала дизайн Dan North BDD История

    Тесты вперед Автоматизация тестов
  29. BDD СЦЕНАРНЫЙ photo by dsearls

  30. photo by dsearls photo by Horia Varlan СЛОВАРЬ

  31. photo by dsearls photo by Horia Varlan для тестеров СЛОВАРЬ

  32. photo by dsearls photo by Horia Varlan для аналитиков СЛОВАРЬ

    для тестеров
  33. photo by dsearls photo by Horia Varlan для девелоперов СЛОВАРЬ

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

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

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

    заказчики ИСКОРЕНИТ множество проблем ДИЗАЙНА и КОММУНИКАЦИЙ 1 СЛОВАРЬ
  37. КОММУНИКАЦИИ photo by joshfassbind.com

  38. In order to [A] As a [B] I need [C]

    Наратив:
  39. Чтобы [A] В качестве [B] Мне нужно [C] Наратив:

  40. A ⎯ добавочное знач. (профит) функционала B ⎯ профитирующая персона

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

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

    удовлетворяет все приемочные критерии, то она работает верно; если не выполняет - неверно.
  43. In order to ... As a ... I need ...

    Story:
  44. Given some initial context (the givens), When an event occurs,

    Then ensure some outcomes. In order to ... As a ... I need ... Story:
  45. 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 ...
  46. 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 ...
  47. UnitTest TDD Spec BDD Scenario BDD Сначала анализ Dan North

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

    Dan North BDD История Тесты вперед Автоматизация тестов +
  49. GHERKINDSL photo by isobel.gordon

  50. 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:
  51. 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
  52. 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
  53. 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
  54. 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
  55. ͳΒ͹ 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
  56. Допустим 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
  57. 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
  58. 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
  59. Приемочные критерии должны быть исполняемы!

  60. None
  61. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим:

    $ pear install behat/behat 3. Инициализируем: $ cd path/to/project && behat --init
  62. Установка 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
  63. # language: ru Функционал: Утилита ls Чтобы узнать содержимое директории

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

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

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

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

    То я должен увидеть: Сценарий: 2 файла в директории Чтобы узнать содержимое директории Как пользователь UNIX Я должен иметь утилиту листинга директорий # language: ru Функционал: Утилита ls """ file_one.txt file_foo.txt """
  68. Допустим я нахожусь в директории “test1” ОПРЕДЕЛЕНИЯШАГОВ

  69. <?php Допустим('/^я нахожусь в директории “(.*)”$/'); ОПРЕДЕЛЕНИЯШАГОВ Допустим я нахожусь

    в директории “test1”
  70. <?php Допустим('/^я нахожусь в директории “(.*)”$/', function() { throw new

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

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

    \Behat\Behat\Exception\Pending(); } ); ??? ОПРЕДЕЛЕНИЯШАГОВ Допустим я нахожусь в директории “test1”
  73. ТИПЫРЕЗУЛЬТАТОВШАГОВ

  74. ТИПЫРЕЗУЛЬТАТОВШАГОВ 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending();

  75. 1. Pending шаг ⎯ который throw new \Behat\Behat\Exception\Pending(); 2. Undefined

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

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

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

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

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

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

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

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

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

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

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

  87. <?php $steps->То('/^я должен увидеть:$/', function($world, $string) { if ($world->output !==

    (string) $string) { throw new \Exception('Неверный вывод'); } } ); ПРОВЕРЯЕМРЕЗУЛЬТАТЫ То я должен увидеть:
  88. <?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('Неверный вывод'); } } ); ПРОВЕРЯЕМРЕЗУЛЬТАТЫ То я должен увидеть: То я должен увидеть: ( )
  89. <?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); } ); ОПРЕДЕЛЕНИЯШАГОВ
  90. <?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); } ) ; ОПРЕДЕЛЕНИЯШАГОВ
  91. 1. Описываем поведение Workflow

  92. $ behat features/ 1. Описываем поведение 2. Проверяем поведение (

    ) Workflow
  93. $ behat features/ 1. Описываем поведение 2. Проверяем поведение (

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

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

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

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

    Прогоняем спеки 3.3. Пишем код 3.2. Прогоняем спеки 3. Реализуем поведение: 2. Проверяем поведение ( ) 4. Проверяем поведение Workflow
  98. Описание web- приложений

  99. M!"#

  100. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим:

    $ pear install behat/mink-beta
  101. <?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();
  102. Новый проект 1. Создаем каркас проекта: $ cd path/to/project &&

    zf ...
  103. Новый проект 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
  104. None
  105. None
  106. Новый проект 1. Создаем каркас проекта: $ cd path/to/project &&

    zf ... 2. Инициализируем B$%&': $ behat --init
  107. Новый проект 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
  108. Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь

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

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

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

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

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

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

  119. http://knplabs.com/trainings http://github.com/behat http://groups.google.com/behat

  120. Вопросы? http://github.com/behat http://knplabs.com/trainings http://groups.google.com/behat