SlideShare una empresa de Scribd logo
1 de 114
BDD
  with
  Symfony2
I
who
 am




      everzet
       senior from-birth PHP
                 developer at
TIMELINE



           UnitTest
                      Test code automatically
TDD
                      Write tests first
TIMELINE



           UnitTest
                      Test code automatically
BDD




                      Dan North
           TDD
                      Write tests first
TIMELINE



           UnitTest
                      Test code automatically
BDD ❿evolution of TDD
- Oh, man, i hate evolutions!
  What’s wrong with TDD?
Test-Driven Development

- Oh, man, i hate evolutions!
  What’s wrong with TDD?
Are we really talking about tests???
               But how to test something, that not exists yet?




  Test-Driven Development

- Oh, man, i hate evolutions!
  What’s wrong with TDD?
In reality, we’re talking bout software design




   Test-Driven Development

 - Oh, man, i hate evolutions!
   What’s wrong with TDD?
Behavior
Test-Driven Development
            © 2003, Dan North
BDD   was introduced as set
        of conventions over   TDD
BDD          was introduced as set
                          of conventions over   TDD
Test method names should be sentences
testFindsCustomerById()                   ❿ tests, that class finds customer by ID
testFailsForDuplicateCustomers()         ❿tests, that class fails for dup customers
BDD            was introduced as set
                            of conventions over       TDD
Test method names should be sentences
testFindsCustomerById()                          ❿ tests, that class finds customer by ID
testFailsForDuplicateCustomers()                ❿tests, that class fails for dup customers

Test method names should start with “should” word
shouldFindCustomerById()                             ❿ class should find customer by ID
shouldFailForDuplicateCustomers()                   ❿class should fail for dup customers
BDD            was introduced as set
                            of conventions over       TDD
Test method names should be sentences
testFindsCustomerById()                          ❿ tests, that class finds customer by ID
testFailsForDuplicateCustomers()                ❿tests, that class fails for dup customers

Test method names should start with “should” word
shouldFindCustomerById()                             ❿ class should find customer by ID
shouldFailForDuplicateCustomers()                   ❿class should fail for dup customers

TestCase class should be nouns in test method sentences
class CustomerTableTest extends PHPUnitTestCase
{
    /**
     * @Test
     */
    shouldFindCustomerById()     ❿ CustomerTable should find customer by ID
    ...
}
ASSERTIONS
                  are TEST-oriented too

                      TESTing

     assertEquals($expected, $actual)

assertGreaterThan($expected, $actual)

    assertInstanceOf($class, $actual)
ASSERTIONS
                  are TEST-oriented too

                      TESTing           Describing

     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
Design first Spec BDD




                                   BDD




                                              Dan North
                                   TDD
                                              Write tests first
TIMELINE



                                   UnitTest
                                              Test code automatically
Specification-oriented
   BDD Frameworks
*Spec


  RSpec   by Dave Astels
*Spec


  RSpec   by Dave Astels



  JSpec   by TJ Holowaychuk
*Spec


  RSpec   by Dave Astels



  JSpec   by TJ Holowaychuk



  Fabulous      by Alex Rudakov
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
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




 Write class SPECIFICATION, not UnitTEST
SCENARIO
ORIENTED

BDD


           photo by dsearls
VOCABULARY




             photophoto by dsearls
                   by Horia Varlan
e rs
                  e st
               r t
             fo


VOCABULARY




                         photophoto by dsearls
                               by Horia Varlan
e rs
                  e st
              r t
            fo


VOCABULARY f
            ora
                  na
                    ly
                      st
                        s
                            photophoto by dsearls
                                  by Horia Varlan
e rs
                                         e st
                                     r t
                                   fo


                       VOCABULARY f
                  rs               ora
               pe                        na
            lo
     e ve                                  ly
    d                                        st
for                                            s
                                                   photophoto by dsearls
                                                         by Horia Varlan
for                                             rs
        bu                                      e
            sin                            e st
                   ess                 r t
                                     fo


                         VOCABULARY f
                  rs                 ora
               pe                          na
            lo
     e ve                                    ly
    d                                          st
for                                              s
                                                     photophoto by dsearls
                                                           by Horia Varlan
for                                             rs
        bu                                      e
                                             st


                           1
            sin                            e
                   ess                 r t
                                     fo


                         VOCABULARY f
                  rs                 ora
               pe                          na
            lo
     e ve                                    ly
    d                                          st
for                                              s
                                                     photophoto by dsearls
                                                           by Horia Varlan
ELIMINATING some of the
 AMBIGUITY and MISCOMMUNICATION


                           testers

business                developers
           VOCABULARY
                           analysts



                                  photophoto by dsearls
                                        by Horia Varlan
MU NICATIONS
COM




                  photo by joshfassbind.com
Story:




  In order to [A]
  As a [B]
  I need [C]
Story:




  In order to [A]
  As a [B]
  I need [C]
  A ❿ benefit or value of the feature
     the
  B ❿ person (or role) who will benefit
     the
  C❿some feature
Story:




  In order to [A]
                           ❿ strength is that it forces you to
                            Its
  As a [B]                    identify the value of delivering a
                                 story when you first define it.

  I need [C]                                    © Dan North




  A ❿ benefit or value of the feature
     the
  B ❿ person (or role) who will benefit
     the
  C❿some feature
Story:




     A story’s behaviour is
     simply its acceptance
     criteria!
     ❿ the system fulfills all the acceptance
       if
     criteria, it’s behaving correctly; if it
     doesn’t, it isn’t.
Story:
In order to ...
As a ...
I need ...
Story:
In order to ...
As a ...
I need ...


      Given some initial context (the givens),
      When an event occurs,
      Then ensure some outcomes.
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.
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.
Scenario BDD
           Design first Spec BDD




                                                  Analyse first
                                   BDD




                                                  Dan North
                                    TDD
                                                  Write tests first
TIMELINE



                                   UnitTest
                                                  Test code automatically
GHERKINDSL



             photo by isobel.gordon
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.
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
# 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
# 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
# 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
# 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
# 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
Acceptance criteria
 should be executable!
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
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                         ...
STEP DEFINITIONS
 Given I have a bank account
STEP DEFINITIONS
 Given I have a bank account
 <?php

 Given('/^I have a bank account$/');
STEP DEFINITIONS
 Given I have a bank account
 <?php

 Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();   ???
   }
 );
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();

2. Undefined step ❿that have no definitions (found)
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();

2. Undefined step ❿that have no definitions (found)

3. Ambiguous step ❿which match multiple definitions
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();

2. Undefined step ❿that have no definitions (found)

3. Ambiguous step ❿which match multiple definitions

4. Failed step ❿that throw Exception();
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();

2. Undefined step ❿that have no definitions (found)

3. Ambiguous step ❿which match multiple definitions

4. Failed step ❿that throw Exception();

5. Skipped step ❿that follows pending/undefined/failed
STEP RESULT TYPES


1. Pending step ❿that throw new BehatBehatExceptionPending();

2. Undefined step ❿that have no definitions (found)

3. Ambiguous step ❿which match multiple definitions

4. Failed step ❿that throw Exception();

5. Skipped step ❿that follows pending/undefined/failed

6. Passed step ❿that doesn’t throw exceptions
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );


 When I deposit 35$
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );


 When I deposit 35$
 <?php

 $steps->When('/^I deposit (d+)$$/',
   function($dollars) {
     // $dollars === 35
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function() {
     throw new BehatBehatExceptionPending();
   }
 );


 When I deposit 35$
 <?php

 $steps->When('/^I deposit (d+)$$/',
   function($dollars) {
     // $dollars === 35
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function($world) {
     throw new BehatBehatExceptionPending();
   }
 );


 When I deposit 35$
 <?php

 $steps->When('/^I deposit (d+)$$/',
   function($world, $dollars) {
     // $dollars === 35
   }
 );
STEP DEFINITIONS
 Given I have a bank account
 <?php

 $steps->Given('/^I have a bank account$/',
   function($world) {
     $world->account = new BankAccount();
   }
 );


 When I deposit 35$
 <?php

 $steps->When('/^I deposit (d+)$$/',
   function($world, $dollars) {
     $world->account->deposit($dollars);
   }
 );
TESTING OUTCOME
 Then I should have 35$
TESTING OUTCOME
 Then I should have 35$
 <?php

 $steps->Then('/^I should have (d+)$$/',
   function($world, $balance) {
     if ($balance !== $world->account->getBalance()) {
       throw new Exception('Wrong balance!');
     }
   }
 );
TESTING OUTCOME
 Then I should have 35$
 <?php

 $steps->Then('/^I should have (d+)$$/',
   function($world, $balance) {
     if ($balance !== $world->account->getBalance()) {
       throw new Exception('Wrong balance!');
     }
   }
 );


 Then I should have 35$                                  (     using
                                                             PHPUnit   )
 <?php

 $steps->Then('/^I should have (d+)$$/',
   function($world, $balance) {
     assertEquals($balance, $world->account->getBalance());
   }
 );
STEP DEFINITIONS
 <?php

 $steps->Given('/^I have a bank account$/',
   function($world) {
     $world->account = new BankAccount();
   }
 );

 $steps->When('/^I deposit (d+)$$/',
   function($world, $dollars) {
     $world->account->deposit($dollars);
   }
 );

 $steps->Then('/^I should have (d+)$$/',
   function($world, $balance) {
     assertEquals($balance, $world->account->getBalance());
   }
 );
STEP DEFINITIONS
 <?php

 $steps->

  Given('/^I have a bank account$/',
    function($world) {
      $world->account = new BankAccount();
    }
  )->

  When('/^I deposit (d+)$$/',
    function($world, $dollars) {
      $world->account->deposit($dollars);
    }
  )->

  Then('/^I should have (d+)$$/',
     function($world, $balance) {
       assertEquals($balance, $world->account->getBalance());
     }
  );
d le
        un
    atB
 e h
B
BehatBundle USAGE
 1. Install:
  http://symfony2bundles.org/Behat/BehatBundle
BehatBundle USAGE
 1. Install:
  http://symfony2bundles.org/Behat/BehatBundle

 2. Setup:
  $ app/console behat:test:bundle --init ApplicationHelloBundle
    .
           src/Application/HelloBundle/Tests/Features ❿write and put your features here
              steps                                   ❿place step definition files here
                  steps.php                           ❿example step definition file
               support                              ❿place support scripts here
                 bootstrap.php                      ❿bootstrap
                 env.php                            ❿environment (context) initialization
BehatBundle USAGE
 1. Install:
  http://symfony2bundles.org/Behat/BehatBundle

 2. Setup:
  $ app/console behat:test:bundle --init ApplicationHelloBundle
    .
           src/Application/HelloBundle/Tests/Features ❿write and put your features here
              steps                                   ❿place step definition files here
                  steps.php                           ❿example step definition file
               support                              ❿place support scripts here
                 bootstrap.php                      ❿bootstrap
                 env.php                            ❿environment (context) initialization


 3. Colorize:
  $ app/console behat:test:bundle ApplicationHelloBundle
BUNDLEDSteps
BUNDLEDSteps
 Browser Steps
  Given   /^I   am on(?: the)? (.*)$/
   When   /^I   go to(?: the)? (.*)$/
   When   /^I   (?:follow|click)(?: the)? "([^"]*)"(?: link)*$/
   When   /^I   go back$/
   When   /^I   go forward$/
   When   /^I   send (POST|PUT|DELETE) to (.*) with:$/
   When   /^I   follow redirect$/
BUNDLEDSteps
 Browser Steps
  Given   /^I   am on(?: the)? (.*)$/
   When   /^I   go to(?: the)? (.*)$/
   When   /^I   (?:follow|click)(?: the)? "([^"]*)"(?: link)*$/
   When   /^I   go back$/
   When   /^I   go forward$/
   When   /^I   send (POST|PUT|DELETE) to (.*) with:$/
   When   /^I   follow redirect$/


 Form Steps
  When    /^I   fill in "([^"]*)" with "([^"]*)"$/
  When    /^I   select "([^"]*)" from "([^"]*)"$/
  When    /^I   uncheck "([^"]*)"$/
  When    /^I   uncheck "([^"]*)"$/
  When    /^I   attach the file at "([^"]*)" to "([^"]*)"$/
  When    /^I   press "([^"]*)" in (.*) form$/
BUNDLEDSteps
 Request Steps
  Then   /^Request   method is (.*)$/
  Then   /^Request   has cookie "([^"]*)"$/
  Then   /^Request   has not cookie "([^"]*)"$/
  Then   /^Request   cookie "([^"]*)" is "([^"]*)"$/
BUNDLEDSteps
 Request Steps
  Then   /^Request   method is (.*)$/
  Then   /^Request   has cookie "([^"]*)"$/
  Then   /^Request   has not cookie "([^"]*)"$/
  Then   /^Request   cookie "([^"]*)" is "([^"]*)"$/


 Response Steps
  Then   /^Response status code is (d+)$/
  Then   /^I should see "([^"]*)"$/
  Then   /^I should not see "([^"]*)"$/
  Then   /^I should see element "([^"]*)"$/
  Then   /^Header "([^"]*)" is set to "([^"]*)"$/
  Then   /^Header "([^"]*)" is not set to "([^"]*)"$/
  Then   /^I was redirected$/
  Then   /^I was not redirected$/
  Then   /^Print output$/
Feature: User logins
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Scenario: Existing user can login
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Scenario: Existing user can login




  Scenario: Non-existing user can’t login
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Scenario: Existing user can login
       Given a site have “everzet” user with “qwerty” password
        And I am on the “/login”
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given a site have “everzet” user with “qwerty” password
       And I am on the “/login”
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Scenario: Existing user can login
       Given a site have “everzet” user with “qwerty” password
        And I am on the “/login”
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given a site have “everzet” user with “qwerty” password
       And I am on the “/login”
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login




  Scenario: Existing user can login
       Given I am on the “/login” page
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given I am on the “/login” page
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have “everzet” user with “qwerty” password




  Scenario: Existing user can login
       Given I am on the “/login” page
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given I am on the “/login” page
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |

  Scenario: Existing user can login
       Given I am on the “/login” page
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given I am on the “/login” page
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |

  Scenario: Existing user can login
       Given I am on the “/login” page
       When I fill in “username” with “everzet”
        And I fill in “password” with “qwerty”
        And I press “login” in login form
       Then I should see “Welcome, everzet”

  Scenario: Non-existing user can’t login
      Given I am on the “/login” page
      When I fill in “username” with “someone”
       And I fill in “password” with “qwerty”
       And I press “login” in login form
      Then I should see “Login or password is incorrect”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |

  Scenario Outline: Only existing users can login
       Given I am on the “/login” page
       When I fill in “username” with “<username>”
        And I fill in “password” with “<password>”
        And I press “login” in login form
       Then I should see “<message>”
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |

  Scenario Outline: Only existing users can login
       Given I am on the “/login” page
       When I fill in “username” with “<username>”
        And I fill in “password” with “<password>”
        And I press “login” in login form
       Then I should see “<message>”

       Examples:
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |

  Scenario Outline: Only existing users can login
       Given I am on the “/login” page
       When I fill in “username” with “<username>”
        And I fill in “password” with “<password>”
        And I press “login” in login form
       Then I should see “<message>”

       Examples:
          | username | password | message                       |
          | everzet | qwerty    | Welcome, everzet              |
          | someone | pa$$word | Login or password is incorrect |
USAGE
 1. Colorize:
  $ app/console behat:test:bundle ApplicationHelloBundle
USAGE
 1. Colorize:
  $ app/console behat:test:bundle ApplicationHelloBundle


 2. Write missing steps:
  <?php

  $steps->Given('/^a site have users:$/',
    function($world, $table) {
      $em = $world->getClient()->
        getKernel()->
        getContainer()->
        get('doctrine.orm.entity_manager');

        // remove all users from test db with EntityManager

        foreach ($table->getRowHash() as $row) {
          // persist new user into test db with EntityManager
          // $row[‘username’] AND $row[‘password’]
        }
        $em->flush();
  });
One more thing



             http://Behat.org
In-browser testing and
other frameworks support
Sahi
Sahi
Goutte
Sahi
Goutte
Selenium
Sahi
Goutte
Selenium
sfBrowser
Sahi
Goutte
Selenium
sfBrowser
Symfony2 Client
Sahi
Goutte
Selenium
sfBrowser
Symfony2 Client




         through one clean API
Mink
<?php
// src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.php
namespace SensioHelloBundleTestsController;

use SymfonyBundleFrameworkBundleTestWebTestCase;
use BehatMink;

class HelloControllerTest extends WebTestCase
{
    public function testIndexWithSymfony2Client()
    {
        $client     = $this->createClient();
        $driver     = new MinkDriverSymfony2ClientDriver($client);
        $session    = new MinkSession($driver);

        $session->visit('/hello/Fabien');
        $page = $session->getPage();

        $this->assertTrue($page->hasContent('Hello Fabien'));
    }

    public function testIndexWithSahi()
    {
        $driver     = new MinkDriverSahiDriver('SAHI_SESSION_ID');
        $session    = new MinkSession($driver);

        $session->visit('/hello/Fabien');
        $page = $session->getPage();

        $this->assertTrue($page->hasContent('Hello Fabien'));
    }
}
<?php
// src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.php
namespace SensioHelloBundleTestsController;

use SymfonyBundleFrameworkBundleTestWebTestCase;
use BehatMink;

class HelloControllerTest extends WebTestCase
{
    public function testIndexWithSymfony2Client()
    {
        $client     = $this->createClient();
        $driver     = new MinkDriverSymfony2ClientDriver($client);
        $session    = new MinkSession($driver);

        $session->visit('/hello/Fabien');
        $page = $session->getPage();

        $this->assertTrue($page->hasContent('Hello Fabien'));
    }

    public function testIndexWithSahi()
    {
        $driver     = new MinkDriverSahiDriver('SAHI_SESSION_ID');
        $session    = new MinkSession($driver);

        $session->visit('/hello/Fabien');
        $page = $session->getPage();

        $this->assertTrue($page->hasContent('Hello Fabien'));
    }
}
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |




  Scenario Outline: Only existing users can login
       Given I am on the “/login” page
       When I fill in “username” with “<username>”
        And I fill in “password” with “<password>”
        And I press “login” in login form
       Then I should see “<message>”

       Examples:
          | username | password | message                       |
          | everzet | qwerty    | Welcome, everzet              |
          | someone | pa$$word | Login or password is incorrect |
Feature: User logins
  In order to have extended abilities
  As a site user
  I need to be able to login

  Background:
       Given a site have users:
          | username | password |
          | everzet | qwerty    |


  @javascript
  Scenario Outline: Only existing users can login
       Given I am on the “/login” page
       When I fill in “username” with “<username>”
        And I fill in “password” with “<password>”
        And I press “login” in login form
       Then I should see “<message>”

       Examples:
          | username | password | message                       |
          | everzet | qwerty    | Welcome, everzet              |
          | someone | pa$$word | Login or password is incorrect |
http://Behat.org




Questions?



http://joind.in/2769

Más contenido relacionado

Más de Konstantin Kudryashov

Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projectsKonstantin Kudryashov
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Konstantin Kudryashov
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Konstantin Kudryashov
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)Konstantin Kudryashov
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICKonstantin Kudryashov
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mockingKonstantin Kudryashov
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsKonstantin Kudryashov
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
LESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentLESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentKonstantin Kudryashov
 
Автоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoАвтоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoKonstantin Kudryashov
 

Más de Konstantin Kudryashov (18)

Modern Agile Project Toolbox
Modern Agile Project ToolboxModern Agile Project Toolbox
Modern Agile Project Toolbox
 
Being effective with legacy projects
Being effective with legacy projectsBeing effective with legacy projects
Being effective with legacy projects
 
Min-Maxing Software Costs
Min-Maxing Software CostsMin-Maxing Software Costs
Min-Maxing Software Costs
 
Modern Project Toolbox
Modern Project ToolboxModern Project Toolbox
Modern Project Toolbox
 
Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast Bridging The Communication Gap, Fast
Bridging The Communication Gap, Fast
 
Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015Min-Maxing Software Costs - Laracon EU 2015
Min-Maxing Software Costs - Laracon EU 2015
 
Moving away from legacy code (AgileCymru)
Moving away from legacy code  (AgileCymru)Moving away from legacy code  (AgileCymru)
Moving away from legacy code (AgileCymru)
 
Taking back BDD
Taking back BDDTaking back BDD
Taking back BDD
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
Decoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DICDecoupling with Design Patterns and Symfony2 DIC
Decoupling with Design Patterns and Symfony2 DIC
 
Design how your objects talk through mocking
Design how your objects talk through mockingDesign how your objects talk through mocking
Design how your objects talk through mocking
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projects
 
Behat 3.0 meetup (March)
Behat 3.0 meetup (March)Behat 3.0 meetup (March)
Behat 3.0 meetup (March)
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
BDD в PHP с Behat и Mink
BDD в PHP с Behat и MinkBDD в PHP с Behat и Mink
BDD в PHP с Behat и Mink
 
BDD для PHP проектов
BDD для PHP проектовBDD для PHP проектов
BDD для PHP проектов
 
LESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend developmentLESS, SASS, HAML: 4 буквы, изменившие frontend development
LESS, SASS, HAML: 4 буквы, изменившие frontend development
 
Автоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью CapistranoАвтоматизируем деплоймент проекта с помощью Capistrano
Автоматизируем деплоймент проекта с помощью Capistrano
 

Último

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoffsammart93
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilV3cube
 

Último (20)

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 

BDD in Symfony2

  • 1. BDD with Symfony2
  • 2. I who am everzet senior from-birth PHP developer at
  • 3. TIMELINE UnitTest Test code automatically
  • 4. TDD Write tests first TIMELINE UnitTest Test code automatically
  • 5. BDD Dan North TDD Write tests first TIMELINE UnitTest Test code automatically
  • 7. - Oh, man, i hate evolutions! What’s wrong with TDD?
  • 8. Test-Driven Development - Oh, man, i hate evolutions! What’s wrong with TDD?
  • 9. Are we really talking about tests??? But how to test something, that not exists yet? Test-Driven Development - Oh, man, i hate evolutions! What’s wrong with TDD?
  • 10. In reality, we’re talking bout software design Test-Driven Development - Oh, man, i hate evolutions! What’s wrong with TDD?
  • 11. Behavior Test-Driven Development © 2003, Dan North
  • 12. BDD was introduced as set of conventions over TDD
  • 13. BDD was introduced as set of conventions over TDD Test method names should be sentences testFindsCustomerById() ❿ tests, that class finds customer by ID testFailsForDuplicateCustomers() ❿tests, that class fails for dup customers
  • 14. BDD was introduced as set of conventions over TDD Test method names should be sentences testFindsCustomerById() ❿ tests, that class finds customer by ID testFailsForDuplicateCustomers() ❿tests, that class fails for dup customers Test method names should start with “should” word shouldFindCustomerById() ❿ class should find customer by ID shouldFailForDuplicateCustomers() ❿class should fail for dup customers
  • 15. BDD was introduced as set of conventions over TDD Test method names should be sentences testFindsCustomerById() ❿ tests, that class finds customer by ID testFailsForDuplicateCustomers() ❿tests, that class fails for dup customers Test method names should start with “should” word shouldFindCustomerById() ❿ class should find customer by ID shouldFailForDuplicateCustomers() ❿class should fail for dup customers TestCase class should be nouns in test method sentences class CustomerTableTest extends PHPUnitTestCase { /** * @Test */ shouldFindCustomerById() ❿ CustomerTable should find customer by ID ... }
  • 16. ASSERTIONS are TEST-oriented too TESTing assertEquals($expected, $actual) assertGreaterThan($expected, $actual) assertInstanceOf($class, $actual)
  • 17. ASSERTIONS are TEST-oriented too TESTing Describing 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
  • 18. Design first Spec BDD BDD Dan North TDD Write tests first TIMELINE UnitTest Test code automatically
  • 19. Specification-oriented BDD Frameworks
  • 20. *Spec RSpec by Dave Astels
  • 21. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk
  • 22. *Spec RSpec by Dave Astels JSpec by TJ Holowaychuk Fabulous by Alex Rudakov
  • 23. 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
  • 24. 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 Write class SPECIFICATION, not UnitTEST
  • 25. SCENARIO ORIENTED BDD photo by dsearls
  • 26. VOCABULARY photophoto by dsearls by Horia Varlan
  • 27. e rs e st r t fo VOCABULARY photophoto by dsearls by Horia Varlan
  • 28. e rs e st r t fo VOCABULARY f ora na ly st s photophoto by dsearls by Horia Varlan
  • 29. e rs e st r t fo VOCABULARY f rs ora pe na lo e ve ly d st for s photophoto by dsearls by Horia Varlan
  • 30. for rs bu e sin e st ess r t fo VOCABULARY f rs ora pe na lo e ve ly d st for s photophoto by dsearls by Horia Varlan
  • 31. for rs bu e st 1 sin e ess r t fo VOCABULARY f rs ora pe na lo e ve ly d st for s photophoto by dsearls by Horia Varlan
  • 32. ELIMINATING some of the AMBIGUITY and MISCOMMUNICATION testers business developers VOCABULARY analysts photophoto by dsearls by Horia Varlan
  • 33. MU NICATIONS COM photo by joshfassbind.com
  • 34. Story: In order to [A] As a [B] I need [C]
  • 35. Story: In order to [A] As a [B] I need [C] A ❿ benefit or value of the feature the B ❿ person (or role) who will benefit the C❿some feature
  • 36. Story: In order to [A] ❿ strength is that it forces you to Its As a [B] identify the value of delivering a story when you first define it. I need [C] © Dan North A ❿ benefit or value of the feature the B ❿ person (or role) who will benefit the C❿some feature
  • 37. Story: A story’s behaviour is simply its acceptance criteria! ❿ the system fulfills all the acceptance if criteria, it’s behaving correctly; if it doesn’t, it isn’t.
  • 38. Story: In order to ... As a ... I need ...
  • 39. Story: In order to ... As a ... I need ... Given some initial context (the givens), When an event occurs, Then ensure some outcomes.
  • 40. 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.
  • 41. 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.
  • 42. Scenario BDD Design first Spec BDD Analyse first BDD Dan North TDD Write tests first TIMELINE UnitTest Test code automatically
  • 43. GHERKINDSL photo by isobel.gordon
  • 44. 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.
  • 45. 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
  • 46. # 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
  • 47. # 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
  • 48. # 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
  • 49. # 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
  • 50. # 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
  • 51. Acceptance criteria should be executable!
  • 52.
  • 53. 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
  • 54. 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 ...
  • 55. STEP DEFINITIONS Given I have a bank account
  • 56. STEP DEFINITIONS Given I have a bank account <?php Given('/^I have a bank account$/');
  • 57. STEP DEFINITIONS Given I have a bank account <?php Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } );
  • 58. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } );
  • 59. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); ??? } );
  • 60. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending();
  • 61. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending(); 2. Undefined step ❿that have no definitions (found)
  • 62. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending(); 2. Undefined step ❿that have no definitions (found) 3. Ambiguous step ❿which match multiple definitions
  • 63. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending(); 2. Undefined step ❿that have no definitions (found) 3. Ambiguous step ❿which match multiple definitions 4. Failed step ❿that throw Exception();
  • 64. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending(); 2. Undefined step ❿that have no definitions (found) 3. Ambiguous step ❿which match multiple definitions 4. Failed step ❿that throw Exception(); 5. Skipped step ❿that follows pending/undefined/failed
  • 65. STEP RESULT TYPES 1. Pending step ❿that throw new BehatBehatExceptionPending(); 2. Undefined step ❿that have no definitions (found) 3. Ambiguous step ❿which match multiple definitions 4. Failed step ❿that throw Exception(); 5. Skipped step ❿that follows pending/undefined/failed 6. Passed step ❿that doesn’t throw exceptions
  • 66. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } );
  • 67. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } ); When I deposit 35$
  • 68. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } ); When I deposit 35$ <?php $steps->When('/^I deposit (d+)$$/', function($dollars) { // $dollars === 35 } );
  • 69. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function() { throw new BehatBehatExceptionPending(); } ); When I deposit 35$ <?php $steps->When('/^I deposit (d+)$$/', function($dollars) { // $dollars === 35 } );
  • 70. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function($world) { throw new BehatBehatExceptionPending(); } ); When I deposit 35$ <?php $steps->When('/^I deposit (d+)$$/', function($world, $dollars) { // $dollars === 35 } );
  • 71. STEP DEFINITIONS Given I have a bank account <?php $steps->Given('/^I have a bank account$/', function($world) { $world->account = new BankAccount(); } ); When I deposit 35$ <?php $steps->When('/^I deposit (d+)$$/', function($world, $dollars) { $world->account->deposit($dollars); } );
  • 72. TESTING OUTCOME Then I should have 35$
  • 73. TESTING OUTCOME Then I should have 35$ <?php $steps->Then('/^I should have (d+)$$/', function($world, $balance) { if ($balance !== $world->account->getBalance()) { throw new Exception('Wrong balance!'); } } );
  • 74. TESTING OUTCOME Then I should have 35$ <?php $steps->Then('/^I should have (d+)$$/', function($world, $balance) { if ($balance !== $world->account->getBalance()) { throw new Exception('Wrong balance!'); } } ); Then I should have 35$ ( using PHPUnit ) <?php $steps->Then('/^I should have (d+)$$/', function($world, $balance) { assertEquals($balance, $world->account->getBalance()); } );
  • 75. STEP DEFINITIONS <?php $steps->Given('/^I have a bank account$/', function($world) { $world->account = new BankAccount(); } ); $steps->When('/^I deposit (d+)$$/', function($world, $dollars) { $world->account->deposit($dollars); } ); $steps->Then('/^I should have (d+)$$/', function($world, $balance) { assertEquals($balance, $world->account->getBalance()); } );
  • 76. STEP DEFINITIONS <?php $steps-> Given('/^I have a bank account$/', function($world) { $world->account = new BankAccount(); } )-> When('/^I deposit (d+)$$/', function($world, $dollars) { $world->account->deposit($dollars); } )-> Then('/^I should have (d+)$$/', function($world, $balance) { assertEquals($balance, $world->account->getBalance()); } );
  • 77. d le un atB e h B
  • 78. BehatBundle USAGE 1. Install: http://symfony2bundles.org/Behat/BehatBundle
  • 79. BehatBundle USAGE 1. Install: http://symfony2bundles.org/Behat/BehatBundle 2. Setup: $ app/console behat:test:bundle --init ApplicationHelloBundle . src/Application/HelloBundle/Tests/Features ❿write and put your features here steps ❿place step definition files here    steps.php ❿example step definition file support ❿place support scripts here bootstrap.php ❿bootstrap env.php ❿environment (context) initialization
  • 80. BehatBundle USAGE 1. Install: http://symfony2bundles.org/Behat/BehatBundle 2. Setup: $ app/console behat:test:bundle --init ApplicationHelloBundle . src/Application/HelloBundle/Tests/Features ❿write and put your features here steps ❿place step definition files here    steps.php ❿example step definition file support ❿place support scripts here bootstrap.php ❿bootstrap env.php ❿environment (context) initialization 3. Colorize: $ app/console behat:test:bundle ApplicationHelloBundle
  • 82. BUNDLEDSteps Browser Steps Given /^I am on(?: the)? (.*)$/ When /^I go to(?: the)? (.*)$/ When /^I (?:follow|click)(?: the)? "([^"]*)"(?: link)*$/ When /^I go back$/ When /^I go forward$/ When /^I send (POST|PUT|DELETE) to (.*) with:$/ When /^I follow redirect$/
  • 83. BUNDLEDSteps Browser Steps Given /^I am on(?: the)? (.*)$/ When /^I go to(?: the)? (.*)$/ When /^I (?:follow|click)(?: the)? "([^"]*)"(?: link)*$/ When /^I go back$/ When /^I go forward$/ When /^I send (POST|PUT|DELETE) to (.*) with:$/ When /^I follow redirect$/ Form Steps When /^I fill in "([^"]*)" with "([^"]*)"$/ When /^I select "([^"]*)" from "([^"]*)"$/ When /^I uncheck "([^"]*)"$/ When /^I uncheck "([^"]*)"$/ When /^I attach the file at "([^"]*)" to "([^"]*)"$/ When /^I press "([^"]*)" in (.*) form$/
  • 84. BUNDLEDSteps Request Steps Then /^Request method is (.*)$/ Then /^Request has cookie "([^"]*)"$/ Then /^Request has not cookie "([^"]*)"$/ Then /^Request cookie "([^"]*)" is "([^"]*)"$/
  • 85. BUNDLEDSteps Request Steps Then /^Request method is (.*)$/ Then /^Request has cookie "([^"]*)"$/ Then /^Request has not cookie "([^"]*)"$/ Then /^Request cookie "([^"]*)" is "([^"]*)"$/ Response Steps Then /^Response status code is (d+)$/ Then /^I should see "([^"]*)"$/ Then /^I should not see "([^"]*)"$/ Then /^I should see element "([^"]*)"$/ Then /^Header "([^"]*)" is set to "([^"]*)"$/ Then /^Header "([^"]*)" is not set to "([^"]*)"$/ Then /^I was redirected$/ Then /^I was not redirected$/ Then /^Print output$/
  • 87. Feature: User logins In order to have extended abilities As a site user I need to be able to login
  • 88. Feature: User logins In order to have extended abilities As a site user I need to be able to login Scenario: Existing user can login
  • 89. Feature: User logins In order to have extended abilities As a site user I need to be able to login Scenario: Existing user can login Scenario: Non-existing user can’t login
  • 90. Feature: User logins In order to have extended abilities As a site user I need to be able to login Scenario: Existing user can login Given a site have “everzet” user with “qwerty” password And I am on the “/login” When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given a site have “everzet” user with “qwerty” password And I am on the “/login” When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 91. Feature: User logins In order to have extended abilities As a site user I need to be able to login Scenario: Existing user can login Given a site have “everzet” user with “qwerty” password And I am on the “/login” When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given a site have “everzet” user with “qwerty” password And I am on the “/login” When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 92. Feature: User logins In order to have extended abilities As a site user I need to be able to login Scenario: Existing user can login Given I am on the “/login” page When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given I am on the “/login” page When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 93. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have “everzet” user with “qwerty” password Scenario: Existing user can login Given I am on the “/login” page When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given I am on the “/login” page When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 94. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario: Existing user can login Given I am on the “/login” page When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given I am on the “/login” page When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 95. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario: Existing user can login Given I am on the “/login” page When I fill in “username” with “everzet” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Welcome, everzet” Scenario: Non-existing user can’t login Given I am on the “/login” page When I fill in “username” with “someone” And I fill in “password” with “qwerty” And I press “login” in login form Then I should see “Login or password is incorrect”
  • 96. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario Outline: Only existing users can login Given I am on the “/login” page When I fill in “username” with “<username>” And I fill in “password” with “<password>” And I press “login” in login form Then I should see “<message>”
  • 97. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario Outline: Only existing users can login Given I am on the “/login” page When I fill in “username” with “<username>” And I fill in “password” with “<password>” And I press “login” in login form Then I should see “<message>” Examples:
  • 98. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario Outline: Only existing users can login Given I am on the “/login” page When I fill in “username” with “<username>” And I fill in “password” with “<password>” And I press “login” in login form Then I should see “<message>” Examples: | username | password | message | | everzet | qwerty | Welcome, everzet | | someone | pa$$word | Login or password is incorrect |
  • 99. USAGE 1. Colorize: $ app/console behat:test:bundle ApplicationHelloBundle
  • 100. USAGE 1. Colorize: $ app/console behat:test:bundle ApplicationHelloBundle 2. Write missing steps: <?php $steps->Given('/^a site have users:$/', function($world, $table) { $em = $world->getClient()-> getKernel()-> getContainer()-> get('doctrine.orm.entity_manager'); // remove all users from test db with EntityManager foreach ($table->getRowHash() as $row) { // persist new user into test db with EntityManager // $row[‘username’] AND $row[‘password’] } $em->flush(); });
  • 101. One more thing http://Behat.org
  • 102. In-browser testing and other frameworks support
  • 103. Sahi
  • 109. Mink
  • 110. <?php // src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.php namespace SensioHelloBundleTestsController; use SymfonyBundleFrameworkBundleTestWebTestCase; use BehatMink; class HelloControllerTest extends WebTestCase { public function testIndexWithSymfony2Client() { $client = $this->createClient(); $driver = new MinkDriverSymfony2ClientDriver($client); $session = new MinkSession($driver); $session->visit('/hello/Fabien'); $page = $session->getPage(); $this->assertTrue($page->hasContent('Hello Fabien')); } public function testIndexWithSahi() { $driver = new MinkDriverSahiDriver('SAHI_SESSION_ID'); $session = new MinkSession($driver); $session->visit('/hello/Fabien'); $page = $session->getPage(); $this->assertTrue($page->hasContent('Hello Fabien')); } }
  • 111. <?php // src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.php namespace SensioHelloBundleTestsController; use SymfonyBundleFrameworkBundleTestWebTestCase; use BehatMink; class HelloControllerTest extends WebTestCase { public function testIndexWithSymfony2Client() { $client = $this->createClient(); $driver = new MinkDriverSymfony2ClientDriver($client); $session = new MinkSession($driver); $session->visit('/hello/Fabien'); $page = $session->getPage(); $this->assertTrue($page->hasContent('Hello Fabien')); } public function testIndexWithSahi() { $driver = new MinkDriverSahiDriver('SAHI_SESSION_ID'); $session = new MinkSession($driver); $session->visit('/hello/Fabien'); $page = $session->getPage(); $this->assertTrue($page->hasContent('Hello Fabien')); } }
  • 112. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | Scenario Outline: Only existing users can login Given I am on the “/login” page When I fill in “username” with “<username>” And I fill in “password” with “<password>” And I press “login” in login form Then I should see “<message>” Examples: | username | password | message | | everzet | qwerty | Welcome, everzet | | someone | pa$$word | Login or password is incorrect |
  • 113. Feature: User logins In order to have extended abilities As a site user I need to be able to login Background: Given a site have users: | username | password | | everzet | qwerty | @javascript Scenario Outline: Only existing users can login Given I am on the “/login” page When I fill in “username” with “<username>” And I fill in “password” with “<password>” And I press “login” in login form Then I should see “<message>” Examples: | username | password | message | | everzet | qwerty | Welcome, everzet | | someone | pa$$word | Login or password is incorrect |

Notas del editor

  1. Hi all, now we&amp;#x2019;ll be talking about BDD in Symfony2.\n
  2. Who am i? I am insane senior from-birth PHP developer from knpLabs, also known as everzet ;-)\nI&amp;#x2019;m Symfony user from 2007 and creator of sfLESSPlugin, Capifony deployment script, RestfulControllersBundle, Jade.php and, ofcourse, Behat, about which we&amp;#x2019;ll be talking today.\n
  3. Little history and timelinening.\nFirst, there was a code. And when codebase become very large, it&amp;#x2019;s become very hard to test it with hands, so someone started to write code to test code. And this new testing code was called UnitTest.\n
  4. Some time later, someone noticed, that it&amp;#x2019;s very hard to motivate people about writing tests post-factum. Also, it&amp;#x2019;s very hard to test systems, that wasn&amp;#x2019;t built with tests in mind. And system, that hard to test, also means, that it hard to use too. So, someone at some point said: &amp;#x201C;Hey. Let&amp;#x2019;s write unit tests first. Before the actual code was even written.&amp;#x201D;. And this drive unit-testing from testing methodology to design one. So now, we&amp;#x2019;re not testing, we&amp;#x2019;re design!\n
  5. But know what? It doesn&amp;#x2019;t work! No-no. Don&amp;#x2019;t get me wrong... People understood &amp;#x201C;test first&amp;#x201D; part. But they totally missunderstood &amp;#x201C;design with tests&amp;#x201D; one. So, in 2003, Dan North described evolutionary methodology called BDD. Or Behavior-Driven Development.\n
  6. So, BDD is a simple evolution of TDD. Not revolution, not replacement - but evolution.\n
  7. Evolution is all about elliminating dinosaurs inside everyone of us.\n
  8. TDD indeed has mistake. And what&amp;#x2019;s even worse, it in it&amp;#x2019;s name!\n
  9. This &amp;#x201C;TEST&amp;#x201D; word. Think for a minute. Are we talking bout tests??? Really! It confuse people. They try to test code, that doesn&amp;#x2019;t exists and in some cases - they can&amp;#x2019;t! Because before test something, we need to DESIGN something.\n
  10. So, in reality, we&amp;#x2019;re talking not about tests, but about software design! And know what? SoftwareDesign-Driven Development is even more stupid therm, than TDD, so\n
  11. In 2003 Dan North proposed to call it Behavior-Driven Development. Cuz really, Behavior of software is most important thing in software itself. Soft without or with wrong Behavior doesn&amp;#x2019;t makes sense!\n
  12. So, Dan started with simple conventions for evolutionize TDD into BDD in mind of developers. Cuz we must start there first.\n
  13. First rule is test method names should be sentences. Cuz this how we&amp;#x2019;re thinking about things. We want to test, that &amp;#x201C;thing must do something&amp;#x201D;, and not &amp;#x201C;testThing!&amp;#x201D;.\n
  14. Another big change is replacement of words &amp;#x201C;test&amp;#x201D; everywhere. That was big mistake with TDD. Developers has taken existsing UnitTesting tools, built for TESTING and started to use them for DESIGN. That&amp;#x2019;s totally wrong! You can&amp;#x2019;t totally concentrate on design, when you&amp;#x2019;re writing word &amp;#x201C;test&amp;#x201D; everywhere - in test names in test class. I even used this word twice to describe this! So, replace it with something more behavioral, like &amp;#x201C;should&amp;#x201D;. Class should do something!\n
  15. So, testcase class should be nouns, which should do something. This means, that your test methods must create full sentenses with testcase class names. In this example - &amp;#x201C;CustomerTable should find customer by ID&amp;#x201D;.\n
  16. But that&amp;#x2019;s not all. Assertions, that we love and use everyday is also test-centic. They focuses on testing, rather than design. We should describe behaviors, not testing something unexistent.\n
  17. So, in better world it should be something like this.\n
  18. And Dan North was not the only one, who believed in this whole new design methodology. Some developers taken all that rules and started to create new tools.\n
  19. So, first BDD frameworks type. Specification-oriented BDD Frameworks. Or just AsteriskSpec.\n
  20. First full-featured Spec framework was Ruby&amp;#x2019;s RSpec, created by Dave Astels and team. He implemented all Dan North proposals, including assertions to should&amp;#x2019;s change (thanks to Ruby OOP model).\n
  21. Second is cool JSpec for JS Spec testing by TJ. By the way, this is the guy, who created Express, Jade and Stylo frameworks for node.js ;-)\n
  22. And the last one, is ofcourse PHP Spec framework by Alex Rudakov, called Fabulous. Yes, we have PHPSpec, but it died right after it was born. And it was by reason. And reason is PHP itself. It hasn&amp;#x2019;t got reach OOP model like Ruby - we can&amp;#x2019;t extend core objects with our native methods, we must use &amp;#x201C;dashgreater&amp;#x201D; or &amp;#x201C;arrow&amp;#x201D; instead of dots and we haven&amp;#x2019;t got reach DSL abilities, that Ruby has. So, i don&amp;#x2019;t really believe in Spec frameworks for PHP. I simply use first three BDD rules in PHPUnit. But if you want to try what is full-stack Spec framework in PHP - use Fabulous. It&amp;#x2019;s PHPUnit extension and it&amp;#x2019;s quite good.\n
  23. Example time. This is RSpec test from rspec site. As you can see, it&amp;#x2019; very descriptive and doesn&amp;#x2019;t looks like UnitTest at all. At first glance... We decribing Bowling score. Describing, that it returns 0 for all gutter game. And in this state, bowling score should be equals to zero. Quite cool!\n
  24. Ok. Let&amp;#x2019;s summarize. When you writing UnitTests (in RSpec, JSpec or still in PHPUnit) - write class specification instead of TEST! You&amp;#x2019;re designing, not testing! Remember it!\n
  25. But Spec BDD is not all. Now, we&amp;#x2019;ll be talking about something really interesting. And it&amp;#x2019;s called Scenario oriented BDD.\nAt the end of 2004, Dan noticed huge amount of analysis parts in previously declared BDD methodology. And after some brainstorming, he decided to apply all of this behavior-driven thinking to defining requirements.\n
  26. The aim of this work was to built consistent vocabulary for\n
  27. testers\n
  28. analysts\n
  29. developers\n
  30. and business people\n
  31. One vocabulary to rule them all! And if it works, then\n
  32. We would be well on the way to eliminating some of the ambiguity and miscommunication that occur when technical people talk to business people. One vocabulary to set communication between business and technical people. So, in contrast with Spec BDD, which focuses on internal product design, Scenario BDD is almost all about\n
  33. communications! And all we know, that this is hardest part in software development. Ofcourse, when we have business people on project. When we not - we&amp;#x2019;re almost happy. But let&amp;#x2019;s be realist, businessman lives inside every one of us, and we need to be able to talk with him too.\n
  34. So, Dan started with simple story template.\n
  35. Where:\nx - the benefit OR value of the feature\ny - the person (role) who will benefit that value from this feature\nz - some feature\n
  36. Its strength is that it forces you to identify the value of a story when you first define it. When there is no real business value for a story, it often comes down to something like &amp;#x201D; . . . I want [some feature] so that [I just do, ok?].&amp;#x201D;\n
  37. A Story&amp;#x2019;s or Feature&amp;#x2019;s behavior is simply its acceptance criteria.\n&amp;#x2013; if the system fulfills all the acceptance criteria, it&amp;#x2019;s behaving correctly; if it doesn&apos;t, it isn&apos;t.\nSo, we need a template to simply capture a story&amp;#x2019;s acceptance criteria.\n
  38. So, Dan started to describe it in terms of scenarios, which took the following form\n
  39. So, Dan started to describe it in terms of scenarios, which took the following form\n
  40. And one feature can have multiple acceptance criterias OR\n
  41. scenarios.\nThat&amp;#x2019;s the all idea of Scenario BDD. Simple enough, i think!\n
  42. But don&amp;#x2019;t mess it with Spec BDD. I personally think, that Spec and Scenario BDD is very different approaches. And if Spec BDD is simple evolution of UnitTesting TDD, then Scenario BDD is evolution of acceptance tests (or functional tests if you like). It&amp;#x2019;s 2 different stages of BDD. And if you&amp;#x2019;re building good system - you need them both. Both unit and functional tests, both class specifications and feature descriptions.\n
  43. Again. Ruby guys. Particulary same guys, who bring RSpec to us, created full-featured DSL (Domain Strict Language), based on Scenario BDD proposals by Dan North. This DSL gets called Gherkin\n
  44. And it&amp;#x2019;s very similar to good old Dan&amp;#x2019;s template. This is Dan North&amp;#x2019;s template\n
  45. This is Gherkin. They simply declared parser and lexer for keywords and added optional titles for features and scenarios. And cool part in DSL&amp;#x2019;s is that we can do anything with it. It&amp;#x2019;s our own custom language. And Gherkin, for example, support internationalization. It has translation to\n
  46. french\n
  47. japanize\n
  48. russian\n
  49. and even english pirate&amp;#x2019;ish\n
  50. As i said already, titles and even feature description is optional. You can omit it if you want, but don&amp;#x2019;t!\n
  51. And yeah. Most important one. We&amp;#x2019;re talking about acceptance criterias. And they, ofcourse should be executable. Gherkin itself was created as part of Cucumber project. Wich is acceptance testing framework, built around Custom BDD DSL, called later Gherkin.\n
  52. And now PHP has all this coolness to with Behat acceptance tester and Behat\\Gherkin parser.\nIt&amp;#x2019;s written in PHP5.3 and uses bunch of great Symfony2 components, such as Console, DependencyInjection and EventDispatcher. Which means, that Behat is not a port, but all-sufficient php project, that&amp;#x2019;s easy to use and extend (thanks to DependencyInjection).\n
  53. Ok. How it works. Let&amp;#x2019;s take a look at simple Gherkin feature. We have feature with title and description and titled scenarios in it with givens (or simply steps). Gherkin parses this into abstract syntax tree and builds\n
  54. something like this. FeatureNode is object with title and description, that has 2 ScenarioNodes with titles, every which has 3 StepNodes.\nAfter parsing, Gherkin passes this structures to Behat itself, which visits every node and runs tests agains it. Steps itself are fine-grained enough to be represented directly in code.\n
  55. Let&amp;#x2019;s try this &amp;#x201C;Given I have a bank account example&amp;#x201D;.\n&amp;#x201C;Given&amp;#x201D; is a keyword and &amp;#x201C;I have a bank account&amp;#x201D; is simple text. We need some technique to match this and later more complicated texts.\n
  56. We will use regular expressions for that. Here you see simple regular expression, that will match only &amp;#x201C;I have a bank account&amp;#x201D; string. But we can define regexp, that will match strings started with or ended with something.\nOk. We matched some step to this definition. What&amp;#x2019;s next? We need to do something!\n
  57. So, second argument to this definition setup call would be simple callback function. Also, we need to hold this definitions inside something, to find them fast.\n
  58. This something is called DefinitionDispatcher. And it&amp;#x2019;s available as $steps variable inside every definitions file.\n
  59. But wait... What&amp;#x2019;s that strange exception get thrown in our callback? Case is, doing something inside our own callback is not enough to call this a test. We need some technique to specify step execution state. Behat has 6 different step types.\n
  60. First one is pending steps. Steps, that throws Pending() exception - gets marked as pending OR TBD steps.\n
  61. Undefined step is a steps, for which Behat can&amp;#x2019;t find proper definition. Or in dev terms - RegEx doesn&amp;#x2019;t match any definition.\n
  62. When single step matches multiple definitions - Behat mark it as Ambiguous, cuz it can&amp;#x2019;t decide which definition to call. So, be accurate when writing definition regexps.\n
  63. Failed step is any step, that throws any other than Pending exception. It means, that if your callback code calls something, that throw exception - step will fail. If your callback throws exception itself - it will fail too.\n
  64. Every step, that follow pending/undefined or failed gets marked as skipped and never gets executed.\n
  65. All other steps - is passed. So, step that doesn&amp;#x2019;t throw exception - passes.\n
  66. So, our &amp;#x201C;I have a bank account&amp;#x201D; step will get pending status.\nLet&amp;#x2019;s add another step definition.\n
  67. We have a bank account and we want to deposit some money to it.\n
  68. \n
  69. Notice, in that case our RegExp has number matcher, which will become first argument of our callback. Every parens matcher in your regexp - will become callback argument. Argument name doesn&amp;#x2019;t matter.\nIn our case, dollars variable will be populated with value 35\nBut wait. What to do with this dollars? We need to maintain some states between our scenario context.\n
  70. We have scenario environment for that. It&amp;#x2019;s simple PHP object, extended from \\stdClass, which means, that it works as variable and closure holder. It gets passed into every callback as first argument. It means, that EVERY callback always has at least one first arguments and it&amp;#x2019;s Environment variable. New environment gets created before every scenario run, so every scenario has it&amp;#x2019;s own context.\n
  71. Let&amp;#x2019;s add a code.\nWe create BankAccount and save it in environment object.\nThen we call &amp;#x201C;deposit&amp;#x201D; method, saved on BankAccount object.\nThat simple. But now, we need to test outcome.\n
  72. That&amp;#x2019;s for what &amp;#x201C;Then&amp;#x201D; steps were invented. Let&amp;#x2019;s write definition for that step.\n
  73. We need to test, that our balance is 35$. And if not - step must fail. We can do this simply by manually comparing 2 variables (matched one and saved in environment one) and throw exception if they not match.\n
  74. OR. We can use PHPUnit assertions, that in case of fail will print beautiful exception output. All you need is 2 require_once statements in behat bootstrap script.\n
  75. All steps together in one file. Looks cool, isn&amp;#x2019;t it?\n
  76. And you can event use chaining for better readability.\n
  77. But we&amp;#x2019;re here because of Symfony2 Bundle, which glues Symfony2 and Behat\n
  78. To install it, visit bundle page on symfony2bundles site\n
  79. Setup project structure\nAll your features will be hosted under Bundle/Tests/Features. Notice, that this time, features starts from uppercase.\n
  80. Run all Bundle features\n
  81. BehatBundle doesn&amp;#x2019;t make sense without bundled step definitions.\nYes, Bundle comes with predefined steps, so in most cases, you will simply write feature without even opening step definitions.\n
  82. We have Browser Steps. Like &amp;#x201C;Given I am on the ...&amp;#x201D;, &amp;#x201C;When I go to ...&amp;#x201D; etc.\n
  83. Form steps. You can fill fields, select variants, check checkboxes and even attach the file in fileinput. Then, simply press form button.\n
  84. Request testing. Test request method, request cookes\n
  85. Or even response statuses. Test response status code, response content or even header. If something wrong happened - simply print response body with &amp;#x201C;Then Print output&amp;#x201D;\n
  86. Let&amp;#x2019;s take a loot at example. We want to implement &amp;#x201C;User logins&amp;#x201D; feature\n
  87. We always start from business value, role and feature description, remember?\n
  88. Then, it&amp;#x2019;s time to define our first scenario title. It must be well-descriptive. &amp;#x201C;Existing user can login&amp;#x201D;\n
  89. Also, let&amp;#x2019;s describe scenario, where non-existing user can&amp;#x2019;t login\n
  90. And add steps.\nWe have users in DB, we are on login page, filling in form fields and pressing button, checking later that response contains needed message.\n
  91. Oh, wait. This 2 steps will populate database with fixtures. And every scenario in this feature will have them. So, let&amp;#x2019;s DRY things little bit.\n
  92. Let&amp;#x2019;s move\n
  93. this step to background section. Steps, added to background section will be executed before every scenarios and will share this scenario environment (or context). But wait! What if later we will want to add some additional users into DB? Then we will add this step again and again, simply replacing username and password. Let&amp;#x2019;s DRY this too.\n
  94. Steps can accept multiline arguments. One of this is table. This table will be passed to definition, from where we will can iterate through data in it... later.\n
  95. And again. Both scenario steps batch looks the same. And when we&amp;#x2019;ll have more than 3 such scenarios - it will be hurt. So, let&amp;#x2019;s refactor it to\n
  96. To scenario outline. We replace all data values with TOKENS. This tokens later will be populated with\n
  97. examples, that is simple\n
  98. Gherkin table. Every row, except header one will be executed as simple scenario.\n
  99. Now it&amp;#x2019;s the time to run our first bundle feature. It has one undefined step (fixture load).\n
  100. Let&amp;#x2019;s define it. BehatBundle extends default Behat environment little bit. So we&amp;#x2019;re able to get application kernel, container or event Doctrine entity manager. Second argument to callback is TableNode. You can get rows hash with &amp;#x201C;getRowHash&amp;#x201D; method. Persist our new users and flash them. That&amp;#x2019;s simple\n
  101. There&amp;#x2019;s one more thing left. The 2 most requested freatures in Behat are.\n
  102. \n
  103. And by in-browser testing lot of people really means Selenium, but personally i prefer Sahi one. It&amp;#x2019;s simplier to configure and use.\n
  104. We can use Goutte to test other applications. Goutte could make requests to any application and return response.\n
  105. And, we should be able to inventionally support Selenium ofcourse\n
  106. And sfBrowser for sf1 functional testing\n
  107. And Symfony2 client, ofcourse. The hardest part in whole this zoo of tools is API. Every tool has it&amp;#x2019;s own, very different API. So we need some level of abstraction above them, to be able to talk with them\n
  108. through one clean API\n
  109. And this tool is called Mink. It&amp;#x2019;s an browser emulation abstraction layer. It defines whole bunch of default actions available on your browser. And it could support almost any tool on the market through Drivers model. Let&amp;#x2019;s see an example.\n
  110. Oh, yeah. Did i mentioned, that Mink is part of Behat, but standalone library itself, so you can use it without Behat in you phpUnit tests, like this one. We have 2 tests. One is using Symfony2 Client component, another one is using Sahi in-browser testing. The most interesting parts here is\n
  111. Driver and session initialization. All work is done inside Mink session. But first, you must create a driver. Then, you create new Mink session and give it your new driver instance. All next steps is the same in two methods. Same API to rule 2 insanely different tools. Cool? But what about Behat?\n
  112. Let&amp;#x2019;s rewrite our old feature.\nDone! Ok. And what if, at some point i&amp;#x2019;ve decided to actually run this test inside browser (by default, mink will use Symfony2Client). Let&amp;#x2019;s rewrite it again...\n
  113. Done!\n
  114. \n