BDD в PHP с Behat и Mink

7,116 views

Published on

История и примеры использования BDD в PHP и ZF с помощью Behat и Mink.

Published in: Technology, Sports

BDD в PHP с Behat и Mink

  1. 1. BDD в php
  2. 2. Якто такой @ everzet senior from-birth web developer в
  3. 3. Якто такой International speaker Разработчик Behat, Mink http://github.com/everzet Разработчик jade.php http://card.everzet.com Разработчик capifony everzet@knplabs.com Core-contributor Symfony2 framework Разработчик плагинов symfony и Symfony2 @ everzet senior from-birth web developer в
  4. 4. BDD, Symfony2 эксперты Активные контрибуторы в open-source проекты Консультанты, аудиторы, тренерыhttp://knplabs.com
  5. 5. История тестирования
  6. 6. История UnitTest Автоматизация тестов
  7. 7. TDD Тесты впередИстория UnitTest Автоматизация тестов
  8. 8. BDD Dan North TDD Тесты впередИстория UnitTest Автоматизация тестов
  9. 9. BDD ⎯ эволюция TDD
  10. 10. - Эволюция-хренолюция... Что не так с TDD?
  11. 11. Test-Driven Development
  12. 12. Мы на самом деле говорим о тестах??? Но каким образом тестировать то, чего еще нет?Test-Driven Development
  13. 13. На самом деле, мы говорим о дизайне Test-Driven Development
  14. 14. BehaviorTest-Driven Development © 2003, Dan North
  15. 15. BDD был создан как набор конвенций поверх TDD
  16. 16. BDD был создан как набор конвенций поверх TDDТест-кейсы должы составлять предложенияtestFindsCustomerById()testFailsForDuplicateCustomers()
  17. 17. BDD был создан как набор конвенций поверх TDDТест-кейсы должы составлять предложенияtestFindsCustomerById()testFailsForDuplicateCustomers()Тест-кейсы должны начинаться со слова “should”shouldFindCustomerById()shouldFailForDuplicateCustomers()
  18. 18. BDD был создан как набор конвенций поверх TDDТест-кейсы должы составлять предложенияtestFindsCustomerById()testFailsForDuplicateCustomers()Тест-кейсы должны начинаться со слова “should”shouldFindCustomerById()shouldFailForDuplicateCustomers()Класс тест-кейсов должен представлять из себя существительное для кейсовclass CustomerTableTest extends PHPUnitTestCase{ /** * @Test */ shouldFindCustomerById() ...}
  19. 19. АССЕРШЕНЫ тоже TEST-ориентированы
  20. 20. АССЕРШЕНЫ тоже TEST-ориентированы ТЕСТируем assertEquals($expected, $actual)assertGreaterThan($expected, $actual) assertInstanceOf($class, $actual)
  21. 21. АССЕРШЕНЫ тоже TEST-ориентированы ТЕСТируем Описываем assertEquals($expected, $actual) $actual should be Equals to $expectedassertGreaterThan($expected, $actual) $actual should be GreaterThan $expected assertInstanceOf($class, $actual) $actual should be InstanceOf $class
  22. 22. Specификационные BDD Фрэймворки
  23. 23. *Spec RSpec by Dave Astels
  24. 24. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk
  25. 25. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk Fabulous by Alex Rudakov
  26. 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. 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. 28. История Сначала дизайн Spec BDD UnitTest TDD BDD Тесты вперед Автоматизация тестов Dan North
  29. 29. СЦЕНАРНЫЙBDD photo by dsearls
  30. 30. СЛ ОВАРЬ photophoto by dsearls by Horia Varlan
  31. 31. о в р с те я те длСЛ ОВАРЬ photophoto by dsearls by Horia Varlan
  32. 32. о в р с те я те длСЛ ОВАРЬ дл яа на ли ти ко в photophoto by dsearls by Horia Varlan
  33. 33. о в р с те я те дл в СЛ ОВАРЬ дл о яа п ер на ло в е ли де тидля ко в photophoto by dsearls by Horia Varlan
  34. 34. для в зак р о азч с те и ко я те в дл в СЛ ОВАРЬ дл о яа п ер на ло в е ли де тидля ко в photophoto by dsearls by Horia Varlan
  35. 35. для в зак р о азч с те 1 и ко я те в дл в СЛ ОВАРЬ дл о яа п ер на ло в е ли де тидля ко в photophoto by dsearls by Horia Varlan
  36. 36. ИСКОРЕНИТ множество проблем ДИЗАЙНА и КОММУНИКАЦИЙ 1 те ры т есзаказчики девелоперы СЛ ОВАРЬ ана лит ики photophoto by dsearls by Horia Varlan
  37. 37. НИК АЦИИК ОММУ photo by joshfassbind.com
  38. 38. Наратив: In order to [A] As a [B] I need [C]
  39. 39. Наратив: Чтобы [A] В качестве [B] Мне нужно [C]
  40. 40. Наратив: Чтобы [A] В качестве [B] Мне нужно [C] A ⎯ добавочное знач. (профит) функционала B ⎯ профитирующая персона (роль) C ⎯ функционал
  41. 41. Наратив: Чтобы [A] В качестве [B] ⎯ Сила данной конструкции в том, что она требует определения профита от функционала еще до его реализации Мне нужно [C] © Dan North A ⎯ добавочное знач. (профит) функционала B ⎯ профитирующая персона (роль) C ⎯ функционал
  42. 42. Поведение story ⎯это ее приемочныйкритерий!⎯ если система удовлетворяет всеприемочные критерии, то она работаетверно; если не выполняет - неверно.
  43. 43. Story:In order to ...As a ...I need ...
  44. 44. Story:In order to ...As a ...I need ... Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
  45. 45. Story:In order to ...As a ...I need ... 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.
  46. 46. Story:In order to ...As a ...I need ... Scenario 1: Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Scenario 2: Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
  47. 47. История Сначала дизайн Spec BDD UnitTest TDD BDD Scenario BDD Сначала анализ Тесты вперед Автоматизация тестов Dan North
  48. 48. История Сначала дизайн Spec BDD + UnitTest TDD BDD Scenario BDD Сначала анализ Тесты вперед Автоматизация тестов Dan North
  49. 49. GHERKINDSL photo by isobel.gordon
  50. 50. Story:In order to ...As a ...I need ... Scenario 1: Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Scenario 2: Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
  51. 51. Feature: Feature descriptionIn order to ...As a ...I need ... Scenario: 1st scenario title Given some initial context (the givens), When an event occurs, Then ensure some outcomes. Scenario: 2nd scenario title Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
  52. 52. feature treeFeature: Feature description 1. featureIn order to ...As a ...I need ... Scenario: 1st scenario title2. scenario Given some initial context (the givens) 3. step When an event occurs ... Then ensure some outcomes ... Scenario: 2nd scenario title2. scenario Given some initial context (the givens) 3. step When an event occurs ... Then ensure some outcomes ...
  53. 53. Feature: Feature descriptionIn order to ...As a ...I need ... Scenario: 1st scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes Scenario: 2nd scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes
  54. 54. # language: frFonctionnalité: Feature descriptionIn order to ...As a ...I need ... Scénario: 1st scenario title Etant donné some initial context (the givens) Lorsque an event occurs Alors ensure some outcomes Scénario: 2nd scenario title Etant donné some initial context (the givens) Lorsque an event occurs Alors ensure some outcomes
  55. 55. # language: ja : Feature descriptionIn order to ...As a ...I need ... : 1st scenario title some initial context (the givens) an event occurs ensure some outcomes : 2nd scenario title some initial context (the givens) an event occurs ensure some outcomes
  56. 56. # language: ruФункционал: Feature descriptionIn order to ...As a ...I need ... Сценарий: 1st scenario title Допустим some initial context (the givens) Когда an event occurs То ensure some outcomes Сценарий: 2nd scenario title Допустим some initial context (the givens) Когда an event occurs То ensure some outcomes
  57. 57. # language: en-pirateAhoy matey!: Feature descriptionIn order to ...As a ...I need ... Heave to: 1st scenario title Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: 2nd scenario title Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes
  58. 58. # language: en-pirateAhoy matey!: Heave to: Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes
  59. 59. Приемочные критериидолжны быть исполняемы!
  60. 60. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим: $ pear install behat/behat 3. Инициализируем: $ cd path/to/project && behat --init
  61. 61. Установка 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
  62. 62. # language: ruФункционал: Утилита lsЧтобы узнать содержимое директорииКак пользователь UNIXЯ должен иметь утилиту листинга директорий
  63. 63. # language: ruФункционал: Утилита lsЧтобы узнать содержимое директорииКак пользователь UNIXЯ должен иметь утилиту листинга директорий Сценарий: 2 файла в директории
  64. 64. # language: ruФункционал: Утилита lsЧтобы узнать содержимое директорииКак пользователь UNIXЯ должен иметь утилиту листинга директорий Сценарий: 2 файла в директории Допустим я нахожусь в директории “test1” Если я исполню “ls” То я должен увидеть: """ file_one.txt file_foo.txt """
  65. 65. # language: ru 1. feature Функционал: Утилита ls Чтобы узнать содержимое директории Как пользователь UNIX Я должен иметь утилиту листинга директорий Сценарий: 2 файла в директории Допустим я нахожусь в директории “test1”2. scenario Если я исполню “ls” То я должен увидеть: """ file_one.txt file_foo.txt """
  66. 66. # language: ruФункционал: Утилита lsЧтобы узнать содержимое директорииКак пользователь UNIXЯ должен иметь утилиту листинга директорий Сценарий: 2 файла в директории Допустим я нахожусь в директории “test1” Если я исполню “ls” То я должен увидеть: """ file_one.txt file_foo.txt """
  67. 67. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1”
  68. 68. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php Допустим(/^я нахожусь в директории “(.*)”$/);
  69. 69. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); } );
  70. 70. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); } );
  71. 71. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); ??? } );
  72. 72. ТИПЫРЕЗУЛЬТАТОВШАГОВ
  73. 73. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
  74. 74. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();2. Undefined шаг ⎯ у которого нет (не найдено) определений
  75. 75. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();2. Undefined шаг ⎯ у которого нет (не найдено) определений3. Ambiguous шаг ⎯ который подпадает под несколько определений
  76. 76. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();2. Undefined шаг ⎯ у которого нет (не найдено) определений3. Ambiguous шаг ⎯ который подпадает под несколько определений4. Failed шаг ⎯ который throw Exception();
  77. 77. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();2. Undefined шаг ⎯ у которого нет (не найдено) определений3. Ambiguous шаг ⎯ который подпадает под несколько определений4. Failed шаг ⎯ который throw Exception();5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии
  78. 78. ТИПЫРЕЗУЛЬТАТОВШАГОВ1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();2. Undefined шаг ⎯ у которого нет (не найдено) определений3. Ambiguous шаг ⎯ который подпадает под несколько определений4. Failed шаг ⎯ который throw Exception();5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии6. Passed шаг ⎯ который не кидает эксепшенов
  79. 79. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); } );
  80. 80. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); } );Если я исполню “ls”
  81. 81. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function() { throw new BehatBehatExceptionPending(); } );Если я исполню “ls” <?php $steps->Если(/^я исполню “(.*)”$/, function($dollars) { throw new BehatBehatExceptionPending(); } );
  82. 82. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function($dir) { // $dir === “test1” } );Если я исполню “ls” <?php $steps->Если(/^я исполню “(.*)”$/, function($command) { // $command === “ls” } );
  83. 83. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function($dir) { chdir(fixtures/ . $dir); } );Если я исполню “ls” <?php $steps->Если(/^я исполню “(.*)”$/, function($command) { exec($command, $output); $output = trim(implode(“n”, $output)); } );
  84. 84. ОПРЕДЕЛЕНИЯШАГОВДопустим я нахожусь в директории “test1” <?php $steps->Допустим(/^я нахожусь в директории “(.*)”$/, function($world, $dir) { chdir(fixtures/ . $dir); } );Если я исполню “ls” <?php $steps->Если(/^я исполню “(.*)”$/, function($world, $command) { exec($command, $output); $world->output = trim(implode(“n”, $output)); } );
  85. 85. ПРОВЕРЯЕМРЕЗУЛЬТАТЫТо я должен увидеть:
  86. 86. ПРОВЕРЯЕМРЕЗУЛЬТАТЫТо я должен увидеть: <?php $steps->То(/^я должен увидеть:$/, function($world, $string) { if ($world->output !== (string) $string) { throw new Exception(Неверный вывод); } } );
  87. 87. ПРОВЕРЯЕМРЕЗУЛЬТАТЫТо я должен увидеть: <?php $steps->То(/^я должен увидеть:$/, function($world, $string) { if ($world->output !== (string) $string) { throw new Exception(Неверный вывод); } } );То я должен увидеть: ( using PHPUnit ) <?php $steps->То(/^я должен увидеть:$/, function($world, $string) { assertEquals((string) $string, $world->output); } );
  88. 88. ОПРЕДЕЛЕНИЯШАГОВ <?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); } );
  89. 89. ОПРЕДЕЛЕНИЯШАГОВ <?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); } ) ;
  90. 90. Workflow 1. Описываем поведение
  91. 91. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/
  92. 92. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/ 3. Реализуем поведение
  93. 93. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/ 3. Реализуем поведение 4. Проверяем поведение
  94. 94. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/ 3. Реализуем поведение 4. Проверяем поведение
  95. 95. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/ 3. Реализуем поведение: 3.1. Пишем спеки 3.2. Прогоняем спеки 3.3. Пишем код 3.4. Прогоняем спеки 4. Проверяем поведение
  96. 96. Workflow 1. Описываем поведение 2. Проверяем поведение ( $ ) behat features/ 3. Реализуем поведение: 3.1. Пишем спеки 3.2. Прогоняем спеки 3.3. Пишем код 3.4. Прогоняем спеки 4. Проверяем поведение
  97. 97. Описание web- приложений
  98. 98. M!"#
  99. 99. Установка 1. Добавляем pear-channel: $ pear channel-discover pear.behat.org 2. Ставим: $ pear install behat/mink-beta
  100. 100. <?phpuse BehatMinkMink, BehatMinkSession, BehatMinkDriverGoutteDriver, BehatMinkDriverSahiDriver;// инициализируем 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();
  101. 101. Новый проект 1. Создаем каркас проекта: $ cd path/to/project && zf ...
  102. 102. Новый проект 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
  103. 103. Новый проект 1. Создаем каркас проекта: $ cd path/to/project && zf ... 2. Инициализируем B$%&: $ behat --init
  104. 104. Новый проект 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
  105. 105. # language: ruФункционал: Альбомы Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь управлять коллекцией альбомов Сценарий: Добавление альбома Допустим я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Cilico" И я должен видеть "Edit"
  106. 106. # language: ruФункционал: Альбомы Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь управлять коллекцией альбомов Сценарий: Добавление альбома Допустим я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Cilico" И я должен видеть "Edit"
  107. 107. # language: ruФункционал: Альбомы Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь управлять коллекцией альбомов Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit"
  108. 108. <?php# features/steps/steps.php$steps->Допустим(/^в базе нет альбомов$/, function($world) { $albums = new Application_Model_DbTable_Albums(); $albums->delete(1); });<?php# features/support/bootstrap.php// Конфигурация и инициализация тестовой среды ZF
  109. 109. # language: ruФункционал: Альбомы Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь управлять коллекцией альбомов Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit"
  110. 110. # language: ruФункционал: Альбомы Чтобы иметь представление об исполнителях Как каталогизатор Я должен уметь управлять коллекцией альбомов @javascript Сценарий: Добавление альбома Допустим в базе нет альбомов И я на странице /index/add Если я ввожу "Pendulum" в поле "Artist" И я ввожу "In Silico" в поле "Title" И нажимаю "Add" То я должен видеть "In Silico" И я должен видеть "Edit"
  111. 111. http://B$%&.org
  112. 112. http://github.com/behathttp://groups.google.com/behat http://knplabs.com/trainings
  113. 113. Вопросы?http://github.com/behathttp://groups.google.com/behat http://knplabs.com/trainings

×