Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Specification by Examples
for Symfony2 applications
About me
Cofounder
Cofounder
on Github
on Twitter
- PHP & Cloud @ Genova - 24 Oct
- Internet Of Things! @ Turin [CFP] - 15...
Repo with all examples
Repo on github
Software delivery
customer wants A
project manager understand B
developers write down C
developers effectively write down ...
Successive refinements of E
E'
E''
E'''
E''''
E'''''
The customer will never obtain an A
The problem is to achieve a
single communication domain
How many points does it have?
Example from: Bridging the communication gap (Gojko Adzic)
10
5
14
9
Effectively, there is no right
answer.
It depends on what you consider as a point
Behaviour-driven development
is a software development process based on test-driven development
(TDD). Behavior-driven dev...
BDD
Although BDD is principally an idea about how software development
should be managed by both business interests and te...
Dan North: What is a story?
Behaviour-driven development is an “outside-in” methodology. It starts
at the outside by ident...
BDD is more than tools, but we
are here in order to use BDD
methodology in our Symfony2
projects
Tools
Behat - Behaviour-driven framework
Mink - Web Acceptance testing framework
Pay attention on Behat version
The latest is 3.0 (at today) but there are so many examples and
docs for 2.5
We will work w...
Here is a story
Feature: a description of this feature
        Scenario: A business situation
            Given a precondi...
Here is a story in Italian
Funzionalità: una generica descrizione di questa funzionalità
        Scenario: una situazione ...
Gherkin is a Business Readable
and Domain Specific Language
that let us describe software’s
behaviour without detailing
ho...
Gherkin supports so many
languages
We are building a common language with
different actors involved into to design
process
There are at least two ways to use behat in
your Symfony2 applications
Per-bundle features
Specific features per bundle
Ap...
Personally prefer a single place for all
features
Mainly because it is so difficult to share the
application bundle strategies to a
customer
Keep the focus on the communica...
behat.yml
default:
    suites:
        default:
            path: %paths.base%/features
            contexts: [BehatMinkEx...
Behat uses a "FeatureContext"
as a clue for join the Gherkin
language with the application
A feature
Feature: An hello world feature example
    Scenario: Just say hello to me!
        When I am on "/hello/walter"...
Every step is a custom method in the
FeatureContext
/** @Given /^I am on "([^"]*)"$/ */
public function goOnPage($url)
{
....
Mink carries different predefined steps
I am on "a page"
I press "Get me in!"
I should see "Ok, you are in"
and more... (C...
But remember that we are building a
common communication domain
Write down a new step that use a mink's steps instead forc...
By Examples
Scenario Outline: Just say hello to everyone!
    When I am on <page>
    Then I should see <hello>
    Exampl...
Expectations
Scenario Outline: Say a better hello to special guests
    When I am on <page>
    Then I should see <hello>
...
If we run it
Feature: An hello world feature example
Scenario Outline: Say a better hello to special guests
    When I am ...
Fix the code
class DefaultController extends Controller
{
    public function indexAction($name)
    {
        if ($name =...
My steps interacts with the entity manager
default:
    suites:
        default:
            path: %paths.base%/features
 ...
Create contexts
bin/behat ‐‐init
Context constructor
/**
 * Behat context class.
 */
class HelloFeatureContext implements SnippetAcceptingContext
{
    pri...
My context clears all before runs
/**
 * @BeforeScenario
 */
public function clearDatabase()
{
    $purger = new ORMPurger...
Load books for this scenario
Feature: My books features
    Scenario: List my books
        Given there are books
        ...
Run it!
(and get the missing step definition)
‐‐‐ HelloFeatureContext has missing steps. Define them with these snippets:
...
My custom step
/**
 * @Given there are books
 */
public function thereAreBooks(TableNode $table)
{
    foreach ($table‐>ge...
Scenarios can share the same background
Feature: My books features
    Background:
        Given there are books
         ...
A story
Scenario: An interested user becomes an active user
    Given an interested user with email "walter.dalmut@gmail.c...
Signup Feature Context
class SignupContext implements SnippetAcceptingContext, MinkAwareContext
    {
        private $min...
GIVEN i am an interested user with email
/**
 * @Given I am an interested user with email :arg1
 */
public function anInte...
WHEN i fill all personal fields
/**
 * @When I fill all personal fields
 */
public function heFillsAllPersonalFields()
{
 ...
THEN I confirm my registration
/**
 * @Then I confirm my registration
 */
public function iConfirmTheRegistration()
{
    ...
THEN I should be registered as an
unconfirmed user
/**
 * @Then I should be registered as an unconfirmed user
 */
public f...
THEN I should receive the reg. email
/**
 * @Then I should receive the registration email
 */
public function heShouldRece...
THEN I should be in the reserved area
/**
 * @Then I should be in the reserved area
 */
public function heShouldBeInTheRes...
There are so many features in Behat
But, remember that the goal is to bridging the communication gap
between actors of the...
Thanks for listening
Any question?
Upcoming SlideShare
Loading in …5
×

Symfony2 Specification by examples

2,083 views

Published on

BDD Symfony2 - Behat - Specification by Examples

Published in: Technology
  • Be the first to comment

Symfony2 Specification by examples

  1. 1. Specification by Examples for Symfony2 applications
  2. 2. About me Cofounder Cofounder on Github on Twitter - PHP & Cloud @ Genova - 24 Oct - Internet Of Things! @ Turin [CFP] - 15 Nov - CloudComputing @ Turin [CFP ASAP] Corley S.r.l. - @CorleyCloud UpCloo LTD - @UpCloo wdalmut walterdalmut www.cloudparty.it internetof.it www.cloudconf.it
  3. 3. Repo with all examples Repo on github
  4. 4. Software delivery customer wants A project manager understand B developers write down C developers effectively write down D customer realize to get E
  5. 5. Successive refinements of E E' E'' E''' E'''' E''''' The customer will never obtain an A
  6. 6. The problem is to achieve a single communication domain
  7. 7. How many points does it have? Example from: Bridging the communication gap (Gojko Adzic)
  8. 8. 10
  9. 9. 5
  10. 10. 14
  11. 11. 9
  12. 12. Effectively, there is no right answer. It depends on what you consider as a point
  13. 13. Behaviour-driven development is a software development process based on test-driven development (TDD). Behavior-driven development combines the general techniques and principles of TDD with ideas from domain-driven design and object- oriented analysis and design to provide software development and management teams with shared tools and a shared process to collaborate on software development. from wikipedia
  14. 14. BDD Although BDD is principally an idea about how software development should be managed by both business interests and technical insight from wikipedia
  15. 15. Dan North: What is a story? Behaviour-driven development is an “outside-in” methodology. It starts at the outside by identifying business outcomes, and then drills down into the feature set that will achieve those outcomes. Each feature is captured as a “story”, which defines the scope of the feature along with its acceptance criteria. This article introduces the BDD approach to defining and identifying stories and their acceptance criteria. Read the article here
  16. 16. BDD is more than tools, but we are here in order to use BDD methodology in our Symfony2 projects
  17. 17. Tools Behat - Behaviour-driven framework Mink - Web Acceptance testing framework
  18. 18. Pay attention on Behat version The latest is 3.0 (at today) but there are so many examples and docs for 2.5 We will work with 3.0
  19. 19. Here is a story Feature: a description of this feature         Scenario: A business situation             Given a precondition             And another precondition             When an actor execute an action             And another action appends             Then some testable result is in place             And another condition should be verified         Scenario: Another business scenario starts
  20. 20. Here is a story in Italian Funzionalità: una generica descrizione di questa funzionalità         Scenario: una situazione di business             Data una precondizione             E ancora una nuova precondizione             Quando un attore esegue una azione             E succede anche un altra azione             Allora esite un risultato testabile             Ed un nuova condizione da verificare         Scenario: Un altro scenario di business da verificare
  21. 21. Gherkin is a Business Readable and Domain Specific Language that let us describe software’s behaviour without detailing how that behaviour is implemented. From the cucumber project
  22. 22. Gherkin supports so many languages We are building a common language with different actors involved into to design process
  23. 23. There are at least two ways to use behat in your Symfony2 applications Per-bundle features Specific features per bundle Application features All features in a single folder
  24. 24. Personally prefer a single place for all features
  25. 25. Mainly because it is so difficult to share the application bundle strategies to a customer Keep the focus on the communication not how we implement the solution
  26. 26. behat.yml default:     suites:         default:             path: %paths.base%/features             contexts: [BehatMinkExtensionContextMinkContext]     extensions:         BehatSymfony2Extension: ~         BehatMinkExtension:             base_url: http://localhost:8000             sessions:                 default:                     symfony2: ~
  27. 27. Behat uses a "FeatureContext" as a clue for join the Gherkin language with the application
  28. 28. A feature Feature: An hello world feature example     Scenario: Just say hello to me!         When I am on "/hello/walter"         Then I should see "Hello Walter"
  29. 29. Every step is a custom method in the FeatureContext /** @Given /^I am on "([^"]*)"$/ */ public function goOnPage($url) { ... }
  30. 30. Mink carries different predefined steps I am on "a page" I press "Get me in!" I should see "Ok, you are in" and more... (CSS selectors etc.)
  31. 31. But remember that we are building a common communication domain Write down a new step that use a mink's steps instead force a new definition of something
  32. 32. By Examples Scenario Outline: Just say hello to everyone!     When I am on <page>     Then I should see <hello>     Examples:         | page              | hello            |         | "/hello/walter"   | "Hello Walter"   |         | "/hello/marco"    | "Hello Marco"    |         | "/hello/giovanni" | "Hello Giovanni" |         | "/hello/martina"  | "Hello Martina"  |
  33. 33. Expectations Scenario Outline: Say a better hello to special guests     When I am on <page>     Then I should see <hello>     Examples:         | page              | hello                   |         | "/hello/fabien"   | "Captain on the bridge" |         | "/hello/walter"   | "Hello Walter"          |         | "/hello/marco"    | "Hello Marco"           |         | "/hello/giovanni" | "Hello Giovanni"        |         | "/hello/martina"  | "Hello Martina"         |
  34. 34. If we run it Feature: An hello world feature example Scenario Outline: Say a better hello to special guests     When I am on <page>     Then I should see <hello>     Examples:     | page              | hello                   |     | "/hello/fabien"   | "Captain on the bridge" |         The text "Captain on the bridge" was not found         anywhere in the text of the current page. (BehatMinkExceptionResponseTextException)     | "/hello/walter"   | "Hello Walter"          |     | "/hello/marco"    | "Hello Marco"           |     | "/hello/giovanni" | "Hello Giovanni"        |     | "/hello/martina"  | "Hello Martina"         | ‐‐‐ Failed scenarios:     features/hello.feature:24
  35. 35. Fix the code class DefaultController extends Controller {     public function indexAction($name)     {         if ($name == "fabien") {             $name = "Captain on the bridge";         }         return $this‐>render('CorleyBaseBundle:Default:index.html.twig', array('name' => $name));     } }
  36. 36. My steps interacts with the entity manager default:     suites:         default:             path: %paths.base%/features             contexts:                 ‐ BehatMinkExtensionContextMinkContext                 ‐ HelloFeatureContext:                     entityManager: '@doctrine.orm.entity_manager'     extensions:         BehatSymfony2Extension: ~         BehatMinkExtension:             base_url: http://localhost:8000             sessions:                 default:                     symfony2: ~
  37. 37. Create contexts bin/behat ‐‐init
  38. 38. Context constructor /**  * Behat context class.  */ class HelloFeatureContext implements SnippetAcceptingContext {     private $entityManager;     /**      * Initializes context.      *      * Every scenario gets its own context object.      * You can also pass arbitrary arguments to the context constructor through behat.yml.      */     public function __construct(EntityManager $entityManager)     {         $this‐>entityManager = $entityManager;     }                         
  39. 39. My context clears all before runs /**  * @BeforeScenario  */ public function clearDatabase() {     $purger = new ORMPurger($this‐>entityManager);     $purger‐>purge(); }
  40. 40. Load books for this scenario Feature: My books features     Scenario: List my books         Given there are books             | title                          | author          |             | Specification by Examples      | Gojko Adzic     |             | Bridging the communication gap | Gojko Adzic     |             | The RSpec Book                 | David Chelimsky |         When I am on "/books"         Then I should see "Specification by Examples"         And I should see "Bridging the communication gap"         And I should see "The RSpec Book"
  41. 41. Run it! (and get the missing step definition) ‐‐‐ HelloFeatureContext has missing steps. Define them with these snippets:     /**     * @Given there are books     */     public function thereAreBooks(TableNode $table)     {         throw new PendingException();     }
  42. 42. My custom step /**  * @Given there are books  */ public function thereAreBooks(TableNode $table) {     foreach ($table‐>getHash() as $row) {         $book = new Book();         $book‐>setTitle($row["title"]);         $book‐>setAuthor($row["author"]);         $this‐>entityManager‐>persist($book);     }     $this‐>entityManager‐>flush(); }
  43. 43. Scenarios can share the same background Feature: My books features     Background:         Given there are books             | title                          | author          |             | Specification by Examples      | Gojko Adzic     |             | Bridging the communication gap | Gojko Adzic     |             | The RSpec Book                 | David Chelimsky |     Scenario: List my books         When I am on "/books"         Then I should see "Specification by Examples"         And I should see "Bridging the communication gap"         And I should see "The RSpec Book"
  44. 44. A story Scenario: An interested user becomes an active user     Given an interested user with email "walter.dalmut@gmail.com"     When he goes to homepage     And he fills all personal fields     Then he confirms the registration     And he should be registered as an unconfirmed user     And he should receive the registration email     And he should be in the reserved area
  45. 45. Signup Feature Context class SignupContext implements SnippetAcceptingContext, MinkAwareContext     {         private $mink;         private $minkParameters;         private $entityManager;     ...         public function setMink(Mink $mink)         {             $this‐>mink = $mink;         }         public function setMinkParameters(array $parameters)         {             $this‐>minkParameters = $parameters;         }     ...
  46. 46. GIVEN i am an interested user with email /**  * @Given I am an interested user with email :arg1  */ public function anInterestedUserWithEmail($email) {     // shared in the feature context     $this‐>email = $email;     // Check that is an interested user and not an existing one     Assert::assertNull(         $this‐>entityManager             ‐>getRepository("CorleyBaseBundle:User")‐>findOneByEmail($email)     ); }
  47. 47. WHEN i fill all personal fields /**  * @When I fill all personal fields  */ public function heFillsAllPersonalFields() {     $this‐>mink‐>getSession()‐>getPage()         ‐>find("css", "#corley_bundle_basebundle_user_email")         ‐>setValue($this‐>email);     $this‐>mink‐>getSession()‐>getPage()         ‐>find("css", "#corley_bundle_basebundle_user_name")         ‐>setValue(rand(0,100000)); }
  48. 48. THEN I confirm my registration /**  * @Then I confirm my registration  */ public function iConfirmTheRegistration() {     $client = $this‐>mink         ‐>getSession()         ‐>getDriver()         ‐>getClient();     $client‐>followRedirects(false);     $this‐>mink‐>getSession()‐>getPage()         ‐>find("css", "#corley_bundle_basebundle_user_submit")‐>click(); }
  49. 49. THEN I should be registered as an unconfirmed user /**  * @Then I should be registered as an unconfirmed user  */ public function heShouldBeRegisteredAsAnUnconfirmedUser() {     $this‐>entityManager‐>clear();     $entity = $this‐>entityManager         ‐>getRepository("CorleyBaseBundle:User")         ‐>findOneByEmail($this‐>email);     Assert::assertNotNull($entity);     Assert::assertFalse($entity‐>isConfirmed()); }
  50. 50. THEN I should receive the reg. email /**  * @Then I should receive the registration email  */ public function heShouldReceiveTheRegistrationEmail() {     $driver = $this‐>mink‐>getSession()‐>getDriver();     if (!$driver instanceof KernelDriver) {         throw new RuntimeException("Only kernel drivers");     }     $profile = $driver‐>getClient()‐>getProfile();     if (false === $profile) {         throw new RuntimeException("Profiler is disabled");     }     $collector = $profile‐>getCollector('swiftmailer');     Assert::assertCount(1, $collector‐>getMessages()); } You can wrap a "getProfiler" in a separate method
  51. 51. THEN I should be in the reserved area /**  * @Then I should be in the reserved area  */ public function heShouldBeInTheReservedArea() {     $client = $this‐>mink         ‐>getSession()         ‐>getDriver()         ‐>getClient();     $client‐>followRedirects(true);     $client‐>followRedirect();     $this‐>mink‐>assertSession()‐>pageTextContains("Hello reserved area!"); } You can wrap the redirection in seperate, reusable steps
  52. 52. There are so many features in Behat But, remember that the goal is to bridging the communication gap between actors of the same project with different backgrounds Do not use it for functional testing (use LiipFunctionalTestBundle for that or anything else)
  53. 53. Thanks for listening Any question?

×