Symfony2 Specification by examples
-
Upload
corley-srl -
Category
Technology
-
view
885 -
download
5
description
Transcript of Symfony2 Specification by examples
![Page 1: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/1.jpg)
Specification by Examplesfor Symfony2 applications
![Page 2: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/2.jpg)
About meCofounder 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. - @CorleyCloudUpCloo LTD - @UpCloo
wdalmutwalterdalmut
www.cloudparty.itinternetof.itwww.cloudconf.it
![Page 4: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/4.jpg)
Software deliverycustomer wants Aproject manager understand Bdevelopers write down Cdevelopers effectively write down Dcustomer realize to get E
![Page 5: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/5.jpg)
Successive refinements of EE'E''E'''E''''E'''''
The customer will never obtain an A
![Page 6: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/6.jpg)
The problem is to achieve asingle communication domain
![Page 7: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/7.jpg)
How many points does it have?Example from: Bridging the communication gap (Gojko Adzic)
![Page 8: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/8.jpg)
10
![Page 9: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/9.jpg)
5
![Page 10: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/10.jpg)
14
![Page 11: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/11.jpg)
9
![Page 12: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/12.jpg)
Effectively, there is no rightanswer.
It depends on what you consider as a point
![Page 13: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/13.jpg)
Behaviour-driven developmentis 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 tocollaborate on software development.
from wikipedia
![Page 14: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/14.jpg)
BDDAlthough BDD is principally an idea about how software developmentshould be managed by both business interests and technical insight
from wikipedia
![Page 15: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/15.jpg)
Dan North: What is a story?Behaviour-driven development is an “outside-in” methodology. It startsat the outside by identifying business outcomes, and then drills downinto the feature set that will achieve those outcomes. Each feature is
captured as a “story”, which defines the scope of the feature along withits acceptance criteria. This article introduces the BDD approach to
defining and identifying stories and their acceptance criteria.Read the article here
![Page 16: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/16.jpg)
BDD is more than tools, but weare here in order to use BDD
methodology in our Symfony2projects
![Page 17: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/17.jpg)
ToolsBehat - Behaviour-driven framework
Mink - Web Acceptance testing framework
![Page 18: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/18.jpg)
Pay attention on Behat versionThe latest is 3.0 (at today) but there are so many examples and
docs for 2.5
We will work with 3.0
![Page 19: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/19.jpg)
Here is a storyFeature: 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
![Page 20: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/20.jpg)
Here is a story in ItalianFunzionalità: 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
![Page 21: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/21.jpg)
Gherkin is a Business Readableand Domain Specific Languagethat let us describe software’s
behaviour without detailinghow that behaviour is
implemented.From the cucumber project
![Page 22: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/22.jpg)
Gherkin supports so manylanguages
We are building a common language withdifferent actors involved into to design
process
![Page 23: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/23.jpg)
There are at least two ways to use behat inyour Symfony2 applications
Per-bundle featuresSpecific features per bundle
Application featuresAll features in a single folder
![Page 24: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/24.jpg)
Personally prefer a single place for allfeatures
![Page 25: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/25.jpg)
Mainly because it is so difficult to share theapplication bundle strategies to a
customerKeep the focus on the communication not
how we implement the solution
![Page 26: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/26.jpg)
behat.ymldefault: suites: default: path: %paths.base%/features contexts: [Behat\MinkExtension\Context\MinkContext] extensions: Behat\Symfony2Extension: ~ Behat\MinkExtension: base_url: http://localhost:8000 sessions: default: symfony2: ~
![Page 27: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/27.jpg)
Behat uses a "FeatureContext"as a clue for join the Gherkin
language with the application
![Page 28: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/28.jpg)
A featureFeature: An hello world feature example
Scenario: Just say hello to me! When I am on "/hello/walter" Then I should see "Hello Walter"
![Page 29: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/29.jpg)
Every step is a custom method in theFeatureContext
/** @Given /̂I am on "([̂"]*)"$/ */public function goOnPage($url){...}
![Page 30: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/30.jpg)
Mink carries different predefined stepsI am on "a page"I press "Get me in!"I should see "Ok, you are in"and more... (CSS selectors etc.)
![Page 31: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/31.jpg)
But remember that we are building acommon communication domain
Write down a new step that use a mink's steps instead force a newdefinition of something
![Page 32: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/32.jpg)
By ExamplesScenario 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" |
![Page 33: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/33.jpg)
ExpectationsScenario 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" |
![Page 34: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/34.jpg)
If we run itFeature: 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. (Behat\Mink\Exception\ResponseTextException) | "/hello/walter" | "Hello Walter" | | "/hello/marco" | "Hello Marco" | | "/hello/giovanni" | "Hello Giovanni" | | "/hello/martina" | "Hello Martina" |
‐‐‐ Failed scenarios:
features/hello.feature:24
![Page 35: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/35.jpg)
Fix the codeclass 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)); }}
![Page 36: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/36.jpg)
My steps interacts with the entity managerdefault: suites: default: path: %paths.base%/features contexts: ‐ Behat\MinkExtension\Context\MinkContext ‐ HelloFeatureContext: entityManager: '@doctrine.orm.entity_manager' extensions: Behat\Symfony2Extension: ~ Behat\MinkExtension: base_url: http://localhost:8000 sessions: default: symfony2: ~
![Page 37: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/37.jpg)
Create contextsbin/behat ‐‐init
![Page 38: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/38.jpg)
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; }
![Page 39: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/39.jpg)
My context clears all before runs/** * @BeforeScenario */public function clearDatabase(){ $purger = new ORMPurger($this‐>entityManager); $purger‐>purge();}
![Page 40: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/40.jpg)
Load books for this scenarioFeature: 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"
![Page 41: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/41.jpg)
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(); }
![Page 42: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/42.jpg)
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();}
![Page 43: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/43.jpg)
Scenarios can share the same backgroundFeature: 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"
![Page 44: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/44.jpg)
A storyScenario: An interested user becomes an active user Given an interested user with email "[email protected]" 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
![Page 45: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/45.jpg)
Signup Feature Contextclass 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; } ...
![Page 46: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/46.jpg)
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) );}
![Page 47: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/47.jpg)
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));}
![Page 48: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/48.jpg)
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();}
![Page 49: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/49.jpg)
THEN I should be registered as anunconfirmed 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());}
![Page 50: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/50.jpg)
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
![Page 51: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/51.jpg)
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
![Page 52: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/52.jpg)
There are so many features in BehatBut, remember that the goal is to bridging the communication gap
between actors of the same project with different backgroundsDo not use it for functional testing
(use LiipFunctionalTestBundle for that or anything else)
![Page 53: Symfony2 Specification by examples](https://reader034.fdocuments.net/reader034/viewer/2022042607/556abe88d8b42acd348b45e5/html5/thumbnails/53.jpg)
Thanks for listening
Any question?