From c46deb5ca2306bf3f4dd54c64646e7476bc2c495 Mon Sep 17 00:00:00 2001 From: bocharsky-bw Date: Thu, 12 Nov 2015 21:43:29 +0200 Subject: [PATCH] Add code blocks to the ch17 (practicing-bdd) --- knpu/practicing-bdd.md | 99 +++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 31 deletions(-) diff --git a/knpu/practicing-bdd.md b/knpu/practicing-bdd.md index df9b9cf..485076f 100644 --- a/knpu/practicing-bdd.md +++ b/knpu/practicing-bdd.md @@ -16,7 +16,7 @@ In the "Add a new product" scenario we're describing a feature that does *not* e on the site yet. We planned this scenario earlier by planning - basically imagining - what the best behavior should be. And oops, I just noticed a typo. This should be: - When I click "New Product" +[[[ code('a6733607eb') ]]] ## Step 2: Execute Behat @@ -30,13 +30,16 @@ scenario: Our mission is clear: code *just* enough to fix a failure, then re-execute Behat and repeat until it's all green. The first failure is from "When I click 'New Product'". That makes sense: that link doesn't exist. But there is something else going on too. -Add an "And print last response" line and try again. +Add an "And print last response" line and try again: ```bash ./vendor/bin/behat features/product_admin.feature:19 ``` -Of course: we also haven't logged in yet. Copy that line from the other scenario. +Of course: we also haven't logged in yet. Copy that line from the other scenario: + +[[[ code('46a95df1ce') ]]] + We didn't think of this during the design phase, but clearly we need to be logged in. Try things again: @@ -50,8 +53,12 @@ Same failure, but now for the right reason: we're missing that link. Time to add Open up the template for this page - `list.html.twig`. A link would look real nice up top. Don't add the `href` yet: just put in the text "New Product" and make it -look nice with some CSS classes and a little icon. Other than some easy-win styling, -I want to do as little work as possible to get each step of the scenario to pass. +look nice with some CSS classes and a little icon: + +[[[ code('0c157ab999') ]]] + +Other than some easy-win styling, I want to do as little work as possible to get +each step of the scenario to pass. Refresh: there's the button. It doesn't go anywhere yet, try it: @@ -66,26 +73,36 @@ called "Name". No surprise: the link doesn't have an `href` and we don't even ha a new products page. To get this step to pass, I guess we need to create that. In `ProductAdminController`, -make a new `public function newAction` and set its URL to `/admin/products/new`. +make a new `public function newAction()` and set its URL to `/admin/products/new`. Name the route `product_new` so we can link to it. Inside the method, render a template -called `product/new.html.twig` Easy enough! +called `product/new.html.twig`: + +[[[ code('06c33f5ef5') ]]] + +Easy enough! In the `product/` directory create that template: `new.html.twig`. Extend the base layout - `layout.html.twig` - and add add a `body` block. Add a `form` tag and make -it submit right back to this same URL with `method="POST"`. +it submit right back to this same URL with `method="POST"`: + +[[[ code('9c389fab72') ]]] I am not going to use Symfony's form system for this because (a) we don't have to and (b) my only goal is to get these tests passing. If you *did* want to use Symfony's form system, this is when you would start doing that! To keep this moving, I'll paste in a bunch of code that creates the three fields -we're referencing in the scenario: `Name`, `Price` and `Description`. The important -thing is that those names match up with the label text and that each label has a `for` -attribute that points to the `id` of its field. This is how mink can find the label -by text and then find its field. +we're referencing in the scenario: `Name`, `Price` and `Description`: + +[[[ code('8a46fd1a12') ]]] + +The important thing is that those names match up with the label text and that each +label has a `for` attribute that points to the `id` of its field. This is how Mink +can find the label by text and then find its field. -At the bottom, we have a save button that matches the second to last step: And I -press "Save". +At the bottom, we have a save button that matches the second to last step: + +[[[ code('ad844e4eab') ]]] Ok, try running the scenario again! @@ -94,7 +111,11 @@ Ok, try running the scenario again! ``` It's still failing in the same spot. This *might* seem weird, but if you debug this, -you'll see that I forgot to fill in the "New Products" href. My bad! +you'll see that I forgot to fill in the "New Products" href: + +[[[ code('ae6f5c14f5') ]]] + +My bad! Run Behat again: @@ -105,12 +126,19 @@ Run Behat again: We got further - it filled out and submitted the form, but didn't see the "Product Created FTW!" flash message. Time to add form processing logic. -Add Symfony's `Request` object as an argument with a type hint. Inside of `newAction` -add a simple `if ($request->isMethod('POST'))`. To be super lazy, what if we cheated -by *not* saving the product and *only* showing that flash message? The site already -has some flash messaging functionality, so add the message that the step is looking -for: `$this->addFlash('success', 'Product created FTW!')`. Finish by redirecting -the user to the product page. Run Behat: +Add Symfony's `Request` object as an argument with a type hint. Inside of `newAction()` +add a simple `if ($request->isMethod('POST'))`: + +[[[ code('2cd1fb65d2') ]]] + +To be super lazy, what if we cheated by *not* saving the product and *only* showing +that flash message? The site already has some flash messaging functionality, so add +the message that the step is looking for: `$this->addFlash('success', 'Product created FTW!')`. +Finish by redirecting the user to the product page: + +[[[ code('ee853f839b') ]]] + +Run Behat: ```bash ./vendor/bin/behat features/product_admin.feature:19 @@ -124,18 +152,23 @@ sometimes, you'll write a scenario and discover later that there's a bug because you forgot to test one part of the behavior. In this case I would improve my scenario before fixing the bug by adding: - And I should see "Veloci-chew toy" +[[[ code('6c3f512bcb') ]]] With this, the scenario won't pass *unless* the product actually shows up on the product list. This is BDD: add a step to the scenario, watch it fail, and *then* code until it passes. To fix this failure, add `$product = new Product();` with code to set the name of -the product. Copy that and repeat for price and description. This is missing validation, -so you would do more work than this in real life, *maybe* with a scenario that guarantees -that validation works. +the product. Copy that and repeat for price and description: + +[[[ code('fc4c546243') ]]] -Finish this with `$em = $this->getDoctrine()->getManager();`, persist and flush. +This is missing validation, so you would do more work than this in real life, +*maybe* with a scenario that guarantees that validation works. + +Finish this with `$em = $this->getDoctrine()->getManager();`, then persist and flush: + +[[[ code('084b343999') ]]] Bug fixed! Try it: @@ -146,12 +179,12 @@ Bug fixed! Try it: It's green! In fact, we can go to the browser, refresh and see the Veloci-chew toy for $20. -But there's a problem: it says that the author is 'anonymous'. This should be 'admin' +But there's a problem: it says that the author is "anonymous". This should be "admin" since I created it under that user. That's definitely a bug: we forgot to set the author in `ProductAdminController`. Ok, we know how to fix bugs using BDD: add a step to prove the bug: - And I should not see "anonymous" +[[[ code('592adb51ac') ]]] This is safe because there should only be the *1* product in the list. Back over to the terminal and run the test: @@ -163,17 +196,21 @@ to the terminal and run the test: Yes! The new step fails, phew! In `ProductAdminController`, set the author when a product is created: -`$product->setAuthor($this->getUser());`. Run Behat again: +`$product->setAuthor($this->getUser());`: + +[[[ code('c72f8d1c88') ]]] + +Run Behat again: ```bash ./vendor/bin/behat features/product_admin.feature:19 ``` -Bug squashed! +Fixed! And that folks, is behavior driven development. It's useful, and a bucket of fun. It forces you to design the behavior of your code. But it also helps you know when you're finished. If the scenario is green, stop coding and over-perfecting things. And yes, *someone* will need to add a designer's touch for a really nice UI, But from a behavioral perspective, this feature does what it needs to do. So move onto what's -next. +next.