2. Я
кто
такой
@everzet
senior from-birth web
developer в
3. Я
кто
такой
International speaker
Разработчик Behat, Mink http://github.com/everzet
Разработчик capifony http://card.everzet.com
Разработчик jade.php everzet@knplabs.com
Контрибьютор Symfony2 framework
Разработчик плагинов для symfony и Symfony2
@everzet senior from-birth web
developer в
16. BDD был создан как набор
конвенций поверх TDD
Тест-кейсы должы составлять предложения
testFindsCustomerById()
testFailsForDuplicateCustomers()
17. BDD был создан как набор
конвенций поверх TDD
Тест-кейсы должы составлять предложения
testFindsCustomerById()
testFailsForDuplicateCustomers()
Тест-кейсы должны начинаться со слова “should”
shouldFindCustomerById()
shouldFailForDuplicateCustomers()
18. BDD был создан как набор
конвенций поверх TDD
Тест-кейсы должы составлять предложения
testFindsCustomerById()
testFailsForDuplicateCustomers()
Тест-кейсы должны начинаться со слова “should”
shouldFindCustomerById()
shouldFailForDuplicateCustomers()
Класс тест-кейсов должен представлять из себя существительное для кейсов
class CustomerTableTest extends PHPUnitTestCase
{
/**
* @Test
*/
shouldFindCustomerById()
...
}
20. АССЕРШЕНЫ
тоже TEST-ориентированы
ТЕСТируем Описываем
assertEquals($expected, $actual) $actual should be Equals to $expected
assertGreaterThan($expected, $actual) $actual should be GreaterThan $expected
assertInstanceOf($class, $actual) $actual should be InstanceOf $class
21. История
Сначала дизайн Spec BDD
UnitTest TDD BDD
Тесты вперед
Автоматизация тестов
Dan North
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
41. Story:
Поведение story ⎯
это ее приемочный
критерий!
⎯ если система удовлетворяет все
приемочные критерии, то она работает
верно; если не выполняет - неверно.
43. Story:
In order to ...
As a ...
I need ...
Given some initial context (the givens),
When an event occurs,
Then ensure some outcomes.
44. 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.
45. 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.
46. История
Сначала дизайн Spec BDD
UnitTest TDD BDD Scenario BDD
Сначала анализ
Тесты вперед
Автоматизация тестов
Dan North
47. История
Сначала дизайн Spec BDD
+
UnitTest TDD BDD Scenario BDD
Сначала анализ
Тесты вперед
Автоматизация тестов
Dan North
49. 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.
50. Feature: Feature description
In 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.
51. feature tree
Feature: Feature description 1. feature
In order to ...
As a ...
I need ...
Scenario: 1st scenario title
2. scenario
Given some initial context (the givens) 3. step
When an event occurs ...
Then ensure some outcomes ...
Scenario: 2nd scenario title
2. scenario
Given some initial context (the givens) 3. step
When an event occurs ...
Then ensure some outcomes ...
52. Feature: Feature description
In 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
53. # language: fr
Fonctionnalité: Feature description
In 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
54. # language: ja
: Feature description
In 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
55. # language: ru
Функционал: Feature description
In 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. # language: en-pirate
Ahoy matey!: Feature description
In 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
57. # language: en-pirate
Ahoy 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
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
63. # language: ru
Функционал: Операции над счетом
Чтобы иметь возможность управлять счетом
В качестве клиента
Я должен иметь возможность выполнять операции
64. # language: ru
Функционал: Операции над счетом
Чтобы иметь возможность управлять счетом
В качестве клиента
Я должен иметь возможность выполнять операции
Сценарий: Депозирование средств
65. # language: ru
Функционал: Операции над счетом
Чтобы иметь возможность управлять счетом
В качестве клиента
Я должен иметь возможность выполнять операции
Сценарий: Депозирование средств
Допустим у меня есть банковский счет
Если я положу на счет 35$
То на моем счету должно быть 35$
66. 1. feature
# language: ru
Функционал: Операции над счетом
Чтобы иметь возможность управлять счетом
В качестве клиента
Я должен иметь возможность выполнять операции
Сценарий: Депозирование средств
2. scenario
Допустим у меня есть банковский счет 3. step
Если я положу на счет 35$ ...
То на моем счету должно быть 35$ ...
69. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
70. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
71. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending(); ???
}
);
74. ТИПЫРЕЗУЛЬТАТОВШАГОВ
1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
2. Undefined шаг ⎯ у которого нет (не найдено) определений
75. ТИПЫРЕЗУЛЬТАТОВШАГОВ
1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
2. Undefined шаг ⎯ у которого нет (не найдено) определений
3. Ambiguous шаг ⎯ который подпадает под несколько определений
76. ТИПЫРЕЗУЛЬТАТОВШАГОВ
1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
2. Undefined шаг ⎯ у которого нет (не найдено) определений
3. Ambiguous шаг ⎯ который подпадает под несколько определений
4. Failed шаг ⎯ который throw Exception();
77. ТИПЫРЕЗУЛЬТАТОВШАГОВ
1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
2. Undefined шаг ⎯ у которого нет (не найдено) определений
3. Ambiguous шаг ⎯ который подпадает под несколько определений
4. Failed шаг ⎯ который throw Exception();
5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии
78. ТИПЫРЕЗУЛЬТАТОВШАГОВ
1. Pending шаг ⎯ который throw new BehatBehatExceptionPending();
2. Undefined шаг ⎯ у которого нет (не найдено) определений
3. Ambiguous шаг ⎯ который подпадает под несколько определений
4. Failed шаг ⎯ который throw Exception();
5. Skipped шаг ⎯ который идет следом за pending/undefined/failed в сценарии
6. Passed шаг ⎯ который не кидает эксепшенов
79. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
80. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
Если я положу на счет 35$
81. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
Если я положу на счет 35$
<?php
$steps->Если('/^я положу на счет (d+)$$/',
function($dollars) {
// $dollars === 35
}
);
82. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function() {
throw new BehatBehatExceptionPending();
}
);
Если я положу на счет 35$
<?php
$steps->Если('/^я положу на счет (d+)$$/',
function($dollars) {
// $dollars === 35
}
);
83. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function($world) {
throw new BehatBehatExceptionPending();
}
);
Если я положу на счет 35$
<?php
$steps->Если('/^я положу на счет (d+)$$/',
function($world, $dollars) {
// $dollars === 35
}
);
84. ОПРЕДЕЛЕНИЯШАГОВ
Допустим у меня есть банковский счет
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function($world) {
$world->account = new BankAccount();
}
);
Если я положу на счет 35$
<?php
$steps->Если('/^я положу на счет (d+)$$/',
function($world, $dollars) {
$world->account->deposit($dollars);
}
);
86. ПРОВЕРЯЕМРЕЗУЛЬТАТЫ
То на моем счету должно быть 35$
<?php
$steps->То('/^на моем счету должно быть (d+)$$/',
function($world, $balance) {
if ($balance !== $world->account->getBalance()) {
throw new Exception('Неверный баланс!');
}
}
);
87. ПРОВЕРЯЕМРЕЗУЛЬТАТЫ
То на моем счету должно быть 35$
<?php
$steps->То('/^на моем счету должно быть (d+)$$/',
function($world, $balance) {
if ($balance !== $world->account->getBalance()) {
throw new Exception('Неверный баланс!');
}
}
);
То на моем счету должно быть 35$ ( using
PHPUnit )
<?php
$steps->То('/^на моем счету должно быть (d+)$$/',
function($world, $balance) {
assertEquals($balance, $world->account->getBalance());
}
);
88. ОПРЕДЕЛЕНИЯШАГОВ
<?php
$steps->Допустим('/^у меня есть банковский счет$/',
function($world) {
$world->account = new BankAccount();
}
);
$steps->Если('/^я положу на счет (d+)$$/',
function($world, $dollars) {
$world->account->deposit($dollars);
}
);
$steps->То('/^на моем счету должно быть (d+)$$/',
function($world, $balance) {
assertEquals($balance, $world->account->getBalance());
}
);
89. ОПРЕДЕЛЕНИЯШАГОВ
<?php
$steps->
Допустим('/^у меня есть банковский счет$/',
function($world) {
$world->account = new BankAccount();
}
)->
Если('/^я положу на счет (d+)$$/',
function($world, $dollars) {
$world->account->deposit($dollars);
}
)->
То('/^на моем счету должно быть (d+)$$/',
function($world, $balance) {
assertEquals($balance, $world->account->getBalance());
}
)
;
98. <?php
use BehatMinkMink,
BehatMinkDriverGoutteDriver,
BehatMinkDriverSahiDriver;
// инициализируем Mink и регистрируем драйверы
$mink = new Mink();
$mink->registerDriver('goutte', new GoutteDriver($startUrl), true);
$mink->registerDriver('javascript', new SahiDriver($startUrl, 'firefox'));
$mink->registerDriver('custom', new MyCustomDriver($startUrl));
// выполняем действия в стандартном драйвере
$mink->switchToDefaultDriver();
$mink->getSession()->getPage()->findLink('Downloads')->click();
echo $mink->getSession()->getPage()->getContent();
// выполняем действия в javascript (Sahi) драйвере
$mink->switchToDriver('javascript');
$mink->getSession()->getPage()->findLink('Downloads')->click();
echo $mink->getSession()->getPage()->getContent();
// выполняем действия в кастомном (MyCustomDriver) драйвере
$mink->switchToDriver('custom');
$mink->getSession()->getPage()->findLink('Downloads')->click();
echo $mink->getSession()->getPage()->getContent();
104. Новый проект
1. Создаем каркас проекта:
$ cd path/to/project && zf ...
2. Инициализируем B$%&':
$ behat --init
105. Новый проект
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
106. # language: ru
Функционал: Альбомы
Чтобы иметь представление об исполнителях
Как каталогизатор
Я должен уметь управлять коллекцией альбомов
Сценарий: Добавление альбома
Допустим я на странице /index/add
Если я ввожу "Pendulum" в поле "Artist"
И я ввожу "In Silico" в поле "Title"
И нажимаю "Add"
То я должен видеть "In Cilico"
И я должен видеть "Edit"
107.
108. # language: ru
Функционал: Альбомы
Чтобы иметь представление об исполнителях
Как каталогизатор
Я должен уметь управлять коллекцией альбомов
Сценарий: Добавление альбома
Допустим я на странице /index/add
Если я ввожу "Pendulum" в поле "Artist"
И я ввожу "In Silico" в поле "Title"
И нажимаю "Add"
То я должен видеть "In Cilico"
И я должен видеть "Edit"
109.
110.
111. # language: ru
Функционал: Альбомы
Чтобы иметь представление об исполнителях
Как каталогизатор
Я должен уметь управлять коллекцией альбомов
Сценарий: Добавление альбома
Допустим в базе нет альбомов
И я на странице /index/add
Если я ввожу "Pendulum" в поле "Artist"
И я ввожу "In Silico" в поле "Title"
И нажимаю "Add"
То я должен видеть "In Silico"
И я должен видеть "Edit"
112.
113. <?php
# features/steps/steps.php
$steps->Допустим('/^в базе нет альбомов$/',
function($world) {
$albums = new Application_Model_DbTable_Albums();
$albums->delete(1);
}
);
<?php
# features/support/bootstrap.php
// Конфигурация и инициализация тестовой среды ZF
114. # language: ru
Функционал: Альбомы
Чтобы иметь представление об исполнителях
Как каталогизатор
Я должен уметь управлять коллекцией альбомов
Сценарий: Добавление альбома
Допустим в базе нет альбомов
И я на странице /index/add
Если я ввожу "Pendulum" в поле "Artist"
И я ввожу "In Silico" в поле "Title"
И нажимаю "Add"
То я должен видеть "In Silico"
И я должен видеть "Edit"
115. # language: ru
Функционал: Альбомы
Чтобы иметь представление об исполнителях
Как каталогизатор
Я должен уметь управлять коллекцией альбомов
@javascript
Сценарий: Добавление альбома
Допустим в базе нет альбомов
И я на странице /index/add
Если я ввожу "Pendulum" в поле "Artist"
И я ввожу "In Silico" в поле "Title"
И нажимаю "Add"
То я должен видеть "In Silico"
И я должен видеть "Edit"