Your SlideShare is downloading. ×
0
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
BDD in Symfony2
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

BDD in Symfony2

16,466

Published on

Quality assurance is one of the most difficult things to implement around software development. Most of time it is left for the final phase of development and very often overlooked entirely. As many …

Quality assurance is one of the most difficult things to implement around software development. Most of time it is left for the final phase of development and very often overlooked entirely. As many experienced web development teams already know, QA needs to be part of the development process from the get-go. Behavior development/testing is just one aspect of quality assurance. And we'll talk about that.

Published in: Technology
5 Comments
80 Likes
Statistics
Notes
No Downloads
Views
Total Views
16,466
On Slideshare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
443
Comments
5
Likes
80
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • Hi all, now we’ll be talking about BDD in Symfony2.\n
  • Who am i? I am insane senior from-birth PHP developer from knpLabs, also known as everzet ;-)\nI’m Symfony user from 2007 and creator of sfLESSPlugin, Capifony deployment script, RestfulControllersBundle, Jade.php and, ofcourse, Behat, about which we’ll be talking today.\n
  • Little history and timelinening.\nFirst, there was a code. And when codebase become very large, it’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
  • Some time later, someone noticed, that it’s very hard to motivate people about writing tests post-factum. Also, it’s very hard to test systems, that wasn’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: “Hey. Let’s write unit tests first. Before the actual code was even written.”. And this drive unit-testing from testing methodology to design one. So now, we’re not testing, we’re design!\n
  • But know what? It doesn’t work! No-no. Don’t get me wrong... People understood “test first” part. But they totally missunderstood “design with tests” one. So, in 2003, Dan North described evolutionary methodology called BDD. Or Behavior-Driven Development.\n
  • So, BDD is a simple evolution of TDD. Not revolution, not replacement - but evolution.\n
  • Evolution is all about elliminating dinosaurs inside everyone of us.\n
  • TDD indeed has mistake. And what’s even worse, it in it’s name!\n
  • This “TEST” word. Think for a minute. Are we talking bout tests??? Really! It confuse people. They try to test code, that doesn’t exists and in some cases - they can’t! Because before test something, we need to DESIGN something.\n
  • So, in reality, we’re talking not about tests, but about software design! And know what? SoftwareDesign-Driven Development is even more stupid therm, than TDD, so\n
  • 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’t makes sense!\n
  • So, Dan started with simple conventions for evolutionize TDD into BDD in mind of developers. Cuz we must start there first.\n
  • First rule is test method names should be sentences. Cuz this how we’re thinking about things. We want to test, that “thing must do something”, and not “testThing!”.\n
  • Another big change is replacement of words “test” everywhere. That was big mistake with TDD. Developers has taken existsing UnitTesting tools, built for TESTING and started to use them for DESIGN. That’s totally wrong! You can’t totally concentrate on design, when you’re writing word “test” everywhere - in test names in test class. I even used this word twice to describe this! So, replace it with something more behavioral, like “should”. Class should do something!\n
  • 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 - “CustomerTable should find customer by ID”.\n
  • But that’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
  • So, in better world it should be something like this.\n
  • 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
  • So, first BDD frameworks type. Specification-oriented BDD Frameworks. Or just AsteriskSpec.\n
  • First full-featured Spec framework was Ruby’s RSpec, created by Dave Astels and team. He implemented all Dan North proposals, including assertions to should’s change (thanks to Ruby OOP model).\n
  • 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
  • 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’t got reach OOP model like Ruby - we can’t extend core objects with our native methods, we must use “dashgreater” or “arrow” instead of dots and we haven’t got reach DSL abilities, that Ruby has. So, i don’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’s PHPUnit extension and it’s quite good.\n
  • Example time. This is RSpec test from rspec site. As you can see, it’ very descriptive and doesn’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
  • Ok. Let’s summarize. When you writing UnitTests (in RSpec, JSpec or still in PHPUnit) - write class specification instead of TEST! You’re designing, not testing! Remember it!\n
  • But Spec BDD is not all. Now, we’ll be talking about something really interesting. And it’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
  • The aim of this work was to built consistent vocabulary for\n
  • testers\n
  • analysts\n
  • developers\n
  • and business people\n
  • One vocabulary to rule them all! And if it works, then\n
  • 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
  • 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’re almost happy. But let’s be realist, businessman lives inside every one of us, and we need to be able to talk with him too.\n
  • So, Dan started with simple story template.\n
  • 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
  • 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 ” . . . I want [some feature] so that [I just do, ok?].”\n
  • A Story’s or Feature’s behavior is simply its acceptance criteria.\n– if the system fulfills all the acceptance criteria, it’s behaving correctly; if it doesn't, it isn't.\nSo, we need a template to simply capture a story’s acceptance criteria.\n
  • So, Dan started to describe it in terms of scenarios, which took the following form\n
  • So, Dan started to describe it in terms of scenarios, which took the following form\n
  • And one feature can have multiple acceptance criterias OR\n
  • scenarios.\nThat’s the all idea of Scenario BDD. Simple enough, i think!\n
  • But don’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’s 2 different stages of BDD. And if you’re building good system - you need them both. Both unit and functional tests, both class specifications and feature descriptions.\n
  • 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
  • And it’s very similar to good old Dan’s template. This is Dan North’s template\n
  • This is Gherkin. They simply declared parser and lexer for keywords and added optional titles for features and scenarios. And cool part in DSL’s is that we can do anything with it. It’s our own custom language. And Gherkin, for example, support internationalization. It has translation to\n
  • french\n
  • japanize\n
  • russian\n
  • and even english pirate’ish\n
  • As i said already, titles and even feature description is optional. You can omit it if you want, but don’t!\n
  • And yeah. Most important one. We’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
  • And now PHP has all this coolness to with Behat acceptance tester and Behat\\Gherkin parser.\nIt’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’s easy to use and extend (thanks to DependencyInjection).\n
  • Ok. How it works. Let’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
  • 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
  • Let’s try this “Given I have a bank account example”.\n“Given” is a keyword and “I have a bank account” is simple text. We need some technique to match this and later more complicated texts.\n
  • We will use regular expressions for that. Here you see simple regular expression, that will match only “I have a bank account” 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’s next? We need to do something!\n
  • 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
  • This something is called DefinitionDispatcher. And it’s available as $steps variable inside every definitions file.\n
  • But wait... What’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
  • First one is pending steps. Steps, that throws Pending() exception - gets marked as pending OR TBD steps.\n
  • Undefined step is a steps, for which Behat can’t find proper definition. Or in dev terms - RegEx doesn’t match any definition.\n
  • When single step matches multiple definitions - Behat mark it as Ambiguous, cuz it can’t decide which definition to call. So, be accurate when writing definition regexps.\n
  • 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
  • Every step, that follow pending/undefined or failed gets marked as skipped and never gets executed.\n
  • All other steps - is passed. So, step that doesn’t throw exception - passes.\n
  • So, our “I have a bank account” step will get pending status.\nLet’s add another step definition.\n
  • We have a bank account and we want to deposit some money to it.\n
  • \n
  • 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’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
  • We have scenario environment for that. It’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’s Environment variable. New environment gets created before every scenario run, so every scenario has it’s own context.\n
  • Let’s add a code.\nWe create BankAccount and save it in environment object.\nThen we call “deposit” method, saved on BankAccount object.\nThat simple. But now, we need to test outcome.\n
  • That’s for what “Then” steps were invented. Let’s write definition for that step.\n
  • 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
  • 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
  • All steps together in one file. Looks cool, isn’t it?\n
  • And you can event use chaining for better readability.\n
  • But we’re here because of Symfony2 Bundle, which glues Symfony2 and Behat\n
  • To install it, visit bundle page on symfony2bundles site\n
  • Setup project structure\nAll your features will be hosted under Bundle/Tests/Features. Notice, that this time, features starts from uppercase.\n
  • Run all Bundle features\n
  • BehatBundle doesn’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
  • We have Browser Steps. Like “Given I am on the ...”, “When I go to ...” etc.\n
  • Form steps. You can fill fields, select variants, check checkboxes and even attach the file in fileinput. Then, simply press form button.\n
  • Request testing. Test request method, request cookes\n
  • Or even response statuses. Test response status code, response content or even header. If something wrong happened - simply print response body with “Then Print output”\n
  • Let’s take a loot at example. We want to implement “User logins” feature\n
  • We always start from business value, role and feature description, remember?\n
  • Then, it’s time to define our first scenario title. It must be well-descriptive. “Existing user can login”\n
  • Also, let’s describe scenario, where non-existing user can’t login\n
  • 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
  • Oh, wait. This 2 steps will populate database with fixtures. And every scenario in this feature will have them. So, let’s DRY things little bit.\n
  • Let’s move\n
  • 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’s DRY this too.\n
  • 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
  • And again. Both scenario steps batch looks the same. And when we’ll have more than 3 such scenarios - it will be hurt. So, let’s refactor it to\n
  • To scenario outline. We replace all data values with TOKENS. This tokens later will be populated with\n
  • examples, that is simple\n
  • Gherkin table. Every row, except header one will be executed as simple scenario.\n
  • Now it’s the time to run our first bundle feature. It has one undefined step (fixture load).\n
  • Let’s define it. BehatBundle extends default Behat environment little bit. So we’re able to get application kernel, container or event Doctrine entity manager. Second argument to callback is TableNode. You can get rows hash with “getRowHash” method. Persist our new users and flash them. That’s simple\n
  • There’s one more thing left. The 2 most requested freatures in Behat are.\n
  • \n
  • And by in-browser testing lot of people really means Selenium, but personally i prefer Sahi one. It’s simplier to configure and use.\n
  • We can use Goutte to test other applications. Goutte could make requests to any application and return response.\n
  • And, we should be able to inventionally support Selenium ofcourse\n
  • And sfBrowser for sf1 functional testing\n
  • And Symfony2 client, ofcourse. The hardest part in whole this zoo of tools is API. Every tool has it’s own, very different API. So we need some level of abstraction above them, to be able to talk with them\n
  • through one clean API\n
  • And this tool is called Mink. It’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’s see an example.\n
  • 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
  • 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
  • Let’s rewrite our old feature.\nDone! Ok. And what if, at some point i’ve decided to actually run this test inside browser (by default, mink will use Symfony2Client). Let’s rewrite it again...\n
  • Done!\n
  • \n
  • Transcript

    • 1. BDD with Symfony2
    • 2. Iwho am everzet senior from-birth PHP developer at
    • 3. TIMELINE UnitTest Test code automatically
    • 4. TDD Write tests firstTIMELINE UnitTest Test code automatically
    • 5. BDD Dan North TDD Write tests firstTIMELINE UnitTest Test code automatically
    • 6. BDD ❿evolution of TDD
    • 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. BehaviorTest-Driven Development © 2003, Dan North
    • 12. BDD was introduced as set of conventions over TDD
    • 13. BDD was introduced as set of conventions over TDDTest method names should be sentencestestFindsCustomerById() ❿ tests, that class finds customer by IDtestFailsForDuplicateCustomers() ❿tests, that class fails for dup customers
    • 14. BDD was introduced as set of conventions over TDDTest method names should be sentencestestFindsCustomerById() ❿ tests, that class finds customer by IDtestFailsForDuplicateCustomers() ❿tests, that class fails for dup customersTest method names should start with “should” wordshouldFindCustomerById() ❿ class should find customer by IDshouldFailForDuplicateCustomers() ❿class should fail for dup customers
    • 15. BDD was introduced as set of conventions over TDDTest method names should be sentencestestFindsCustomerById() ❿ tests, that class finds customer by IDtestFailsForDuplicateCustomers() ❿tests, that class fails for dup customersTest method names should start with “should” wordshouldFindCustomerById() ❿ class should find customer by IDshouldFailForDuplicateCustomers() ❿class should fail for dup customersTestCase class should be nouns in test method sentencesclass 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 $expectedassertGreaterThan($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 firstTIMELINE 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. SCENARIOORIENTEDBDD photo by dsearls
    • 26. VOCABULARY photophoto by dsearls by Horia Varlan
    • 27. e rs e st r t foVOCABULARY photophoto by dsearls by Horia Varlan
    • 28. e rs e st r t foVOCABULARY 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 stfor 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 stfor 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 stfor s photophoto by dsearls by Horia Varlan
    • 32. ELIMINATING some of the AMBIGUITY and MISCOMMUNICATION testersbusiness developers VOCABULARY analysts photophoto by dsearls by Horia Varlan
    • 33. MU NICATIONSCOM 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 firstTIMELINE 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 descriptionIn order to ...As a ...I need ... Scenario: 1st scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes Scenario: 2nd scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes
    • 46. # language: frFonctionnalité: Feature descriptionIn order to ...As a ...I need ... Scénario: 1st scenario title Etant donné some initial context (the givens) Lorsque an event occurs Alors ensure some outcomes Scénario: 2nd scenario title Etant donné some initial context (the givens) Lorsque an event occurs Alors ensure some outcomes
    • 47. # language: ja : Feature descriptionIn order to ...As a ...I need ... : 1st scenario title some initial context (the givens) an event occurs ensure some outcomes : 2nd scenario title some initial context (the givens) an event occurs ensure some outcomes
    • 48. # language: ruФункционал: Feature descriptionIn order to ...As a ...I need ... Сценарий: 1st scenario title Допустим some initial context (the givens) Когда an event occurs То ensure some outcomes Сценарий: 2nd scenario title Допустим some initial context (the givens) Когда an event occurs То ensure some outcomes
    • 49. # language: en-pirateAhoy matey!: Feature descriptionIn order to ...As a ...I need ... Heave to: 1st scenario title Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: 2nd scenario title Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes
    • 50. # language: en-pirateAhoy matey!: Heave to: Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes Heave to: Let go and haul some initial context (the givens) Blimey! an event occurs Aye ensure some outcomes
    • 51. Acceptance criteria should be executable!
    • 52. Feature: Feature descriptionIn order to ...As a ...I need ... Scenario: 1st scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes Scenario: 2nd scenario title Given some initial context (the givens) When an event occurs Then ensure some outcomes
    • 53. feature treeFeature: Feature description 1. featureIn order to ...As a ...I need ... Scenario: 1st scenario title2. scenario Given some initial context (the givens) 3. step When an event occurs ... Then ensure some outcomes ... Scenario: 2nd scenario title2. scenario Given some initial context (the givens) 3. step When an event occurs ... Then ensure some outcomes ...
    • 54. STEP DEFINITIONS Given I have a bank account
    • 55. STEP DEFINITIONS Given I have a bank account <?php Given(/^I have a bank account$/);
    • 56. STEP DEFINITIONS Given I have a bank account <?php Given(/^I have a bank account$/, function() { throw new BehatBehatExceptionPending(); } );
    • 57. STEP DEFINITIONS Given I have a bank account <?php $steps->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 RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();
    • 60. STEP RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();2. Undefined step ❿that have no definitions (found)
    • 61. STEP RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();2. Undefined step ❿that have no definitions (found)3. Ambiguous step ❿which match multiple definitions
    • 62. STEP RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();2. Undefined step ❿that have no definitions (found)3. Ambiguous step ❿which match multiple definitions4. Failed step ❿that throw Exception();
    • 63. STEP RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();2. Undefined step ❿that have no definitions (found)3. Ambiguous step ❿which match multiple definitions4. Failed step ❿that throw Exception();5. Skipped step ❿that follows pending/undefined/failed
    • 64. STEP RESULT TYPES1. Pending step ❿that throw new BehatBehatExceptionPending();2. Undefined step ❿that have no definitions (found)3. Ambiguous step ❿which match multiple definitions4. Failed step ❿that throw Exception();5. Skipped step ❿that follows pending/undefined/failed6. Passed step ❿that doesn’t throw exceptions
    • 65. STEP DEFINITIONS Given I have a bank account <?php $steps->Given(/^I have a bank account$/, function() { throw new BehatBehatExceptionPending(); } );
    • 66. STEP DEFINITIONS Given I have a bank account <?php $steps->Given(/^I have a bank account$/, function() { throw new BehatBehatExceptionPending(); } ); When I deposit 35$
    • 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$ <?php $steps->When(/^I deposit (d+)$$/, function($dollars) { // $dollars === 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($world) { throw new BehatBehatExceptionPending(); } ); When I deposit 35$ <?php $steps->When(/^I deposit (d+)$$/, function($world, $dollars) { // $dollars === 35 } );
    • 70. 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); } );
    • 71. TESTING OUTCOME Then I should have 35$
    • 72. 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!); } } );
    • 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!); } } ); Then I should have 35$ ( using PHPUnit ) <?php $steps->Then(/^I should have (d+)$$/, function($world, $balance) { assertEquals($balance, $world->account->getBalance()); } );
    • 74. 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()); } );
    • 75. 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()); } );
    • 76. d le un atB e hB
    • 77. BehatBundle USAGE 1. Install: http://symfony2bundles.org/Behat/BehatBundle
    • 78. 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
    • 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 3. Colorize: $ app/console behat:test:bundle ApplicationHelloBundle
    • 80. BUNDLEDSteps
    • 81. 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$/
    • 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$/ 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$/
    • 83. BUNDLEDSteps Request Steps Then /^Request method is (.*)$/ Then /^Request has cookie "([^"]*)"$/ Then /^Request has not cookie "([^"]*)"$/ Then /^Request cookie "([^"]*)" is "([^"]*)"$/
    • 84. 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$/
    • 85. Feature: User logins
    • 86. Feature: User logins In order to have extended abilities As a site user I need to be able to login
    • 87. 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
    • 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 Scenario: Non-existing user can’t 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 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”
    • 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 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”
    • 92. 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”
    • 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 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”
    • 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 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>”
    • 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>” Examples:
    • 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: | username | password | message | | everzet | qwerty | Welcome, everzet | | someone | pa$$word | Login or password is incorrect |
    • 98. USAGE 1. Colorize: $ app/console behat:test:bundle ApplicationHelloBundle
    • 99. 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(); });
    • 100. One more thing http://Behat.org
    • 101. In-browser testing andother frameworks support
    • 102. Sahi
    • 103. SahiGoutte
    • 104. SahiGoutteSelenium
    • 105. SahiGoutteSeleniumsfBrowser
    • 106. SahiGoutteSeleniumsfBrowserSymfony2 Client
    • 107. SahiGoutteSeleniumsfBrowserSymfony2 Client through one clean API
    • 108. Mink
    • 109. <?php// src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.phpnamespace 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)); }}
    • 110. <?php// src/Sensio/HelloBundle/Tests/Controller/HelloControllerTest.phpnamespace 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. 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 |
    • 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 | @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 |
    • 113. http://Behat.orgQuestions?http://joind.in/2769

    ×