Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Adoptez le TDD sur vos projets Symfony2 existants

Adoptez le TDD sur vos projets Symfony2 existants

Benjamin Grandfond

April 04, 2013
Tweet

More Decks by Benjamin Grandfond

Other Decks in Programming

Transcript

  1. "Clean code that works" Ron Je!ries un des trois fondateurs

    de l’ «Extreme Programming» (XP) 9
  2. RED REFACTOR 1. écrire un test simple qui échoue 3.

    éliminer la duplication GREEN 2. écrire le minimum de code pour faire passer le test Mantra 10
  3. 11 + moins de debug + code découplé + plus

    de con!ance - prend du temps (au début) - plus de code (?) Avantages Inconvénients
  4. 12 • patience, courage, persévérance • intégration continue • exécution

    des tests rapide • qualité des tests (lisibilité, conventions...) Pré requis pour le succès
  5. Suite de Fibonacci class FibonacciTest extends \PHPUnit_Framework_TestCase { public function

    testFibonacci() { $fibonacci = new Fibonacci(); $this->assertEquals(0, $fibonacci->fib(0)); } } RED 13
  6. class Fibonacci { public function fib($value) { return 0; }

    } Fake it! Suite de Fibonacci GREEN 14
  7. class FibonacciTest extends \PHPUnit_Framework_TestCase { public function testFibonacci() { $fibonacci

    = new Fibonacci(); $this->assertEquals(0, $fibonacci->fib(0)); $this->assertEquals(1, $fibonacci->fib(1)); } } Suite de Fibonacci RED 15 class FibonacciTest extends \PHPUnit_Framework_TestCase { public function testFibonacci() { $fibonacci = new Fibonacci(); $this->assertEquals(0, $fibonacci->fib(0)); $this->assertEquals(1, $fibonacci->fib(1)); } } duplication
  8. class Fibonacci { public function fib($value) { if ($value ===

    0) { return 0; } return 1; } } Suite de Fibonacci GREEN 16
  9. class FibonacciTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider getValues */

    public function testFibonacci($value, $result) { $fibonacci = new Fibonacci(); $this->assertEquals($result, $fibonacci->fib($value)); } public function getValues() { return array( array(0, 0), array(1, 1), ); } } Suite de Fibonacci REFACTOR 17
  10. class FibonacciTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider getValues */

    public function testFibonacci($value, $result) { $fibonacci = new Fibonacci(); $this->assertEquals($result, $fibonacci->fib($value)); } public function getValues() { return array( array(0, 0), array(1, 1), ); } } Suite de Fibonacci REFACTOR 17
  11. class FibonacciTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider getValues */

    public function testFibonacci($value, $result) { $fibonacci = new Fibonacci(); $this->assertEquals($result, $fibonacci->fib($value)); } public function getValues() { return array( array(0, 0), array(1, 1), ); } } Suite de Fibonacci REFACTOR 17
  12. class FibonacciTest extends \PHPUnit_Framework_TestCase { /** * @dataProvider getValues */

    public function testFibonacci($value, $result) { $fibonacci = new Fibonacci(); $this->assertEquals($result, $fibonacci->fib($value)); } public function getValues() { return array( array(0, 0), array(1, 1), ); } } Suite de Fibonacci REFACTOR 17
  13. public function getValues() { return array( array(0, 0), array(1, 1),

    array(2, 1), ); } Suite de Fibonacci GREEN 18
  14. public function getValues() { return array( array(0, 0), array(1, 1),

    array(2, 1), array(3, 2), ); } RED Triangulation Suite de Fibonacci 19
  15. class Fibonacci { public function fib($value) { if ($value ===

    0) { return 0; } if ($value <= 2) { return 1; } return 2; } } GREEN Suite de Fibonacci 20
  16. class Fibonacci { public function fib($value) { if ($value ===

    0) { return 0; } if ($value <= 2) { return 1; } return 1 + 1; } } Suite de Fibonacci REFACTOR 21
  17. class Fibonacci { public function fib($value) { if ($value ===

    0) { return 0; } if ($value <= 2) { return 1; } return $this->fib($value - 1) + $this->fib($value - 2); } } REFACTOR Suite de Fibonacci 22
  18. • récupérer les experts dans la base de données •

    créer un template qui a!che la liste • créer un contrôleur • ajouter une nouvelle route Ce qu’on veut : Créer une nouvelle page 25
  19. 1. code retour de la page = 200 2. la

    réponse contient une liste 3. le nombre de li = le nombre d’experts dans la base Ce qu’on va tester : test list Créer une nouvelle page 26
  20. Créer une nouvelle page RED 27 <?php namespace Theodo\Bundle\ExpertBundle\Tests\Controller; use

    Symfony\Bundle\FrameworkBundle\Test\WebTestCase; /** * ExpertControllerTest * * @author Benjamin Grandfond <[email protected]> * @group functional */ class ExpertControllerTest extends WebTestCase { public function testShouldDisplayTheExpertsList() { $client = self::createClient(); $crawler = $client->request('GET', '/experts'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan(1, $crawler->filter('li')->count()); } }
  21. Créer une nouvelle page RED 27 <?php namespace Theodo\Bundle\ExpertBundle\Tests\Controller; use

    Symfony\Bundle\FrameworkBundle\Test\WebTestCase; /** * ExpertControllerTest * * @author Benjamin Grandfond <[email protected]> * @group functional */ class ExpertControllerTest extends WebTestCase { public function testShouldDisplayTheExpertsList() { $client = self::createClient(); $crawler = $client->request('GET', '/experts'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan(1, $crawler->filter('li')->count()); } } TIP
  22. 1. créer la route 2. créer le contrôleur et l’action

    3. créer un template avec une liste et deux éléments Il faut que le test passe avec un minimum de code Créer une nouvelle page 30
  23. RED namespace Theodo\Bundle\ExpertBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration as Extra; class

    ExpertController extends Controller { /** * @Extra\Route("/experts") * @Extra\Template() */ public function listAction() { return array(); } } {% extends '::base.html.twig' %} {% block body %} <ul> <li>Marek</li> <li>Pierre-Henri</li> </ul> {% endblock %} Créer une nouvelle page 31
  24. {% extends '::base.html.twig' %} {% block body %} <ul> {%

    for expert in experts %} <li>{{ expert }}</li> {% endfor %} </ul> {% endblock %} On fait en sorte que les experts soient dynamiques public function listAction() { $experts = array( 'Marek', 'Pierre-Henri', ); return array( 'experts' => $experts ); } REFACTOR Créer une nouvelle page 33
  25. ✔ code retour de la page = 200 ✔ la

    réponse contient une liste ✘ le nombre de li = le nombre d’experts dans la base Ce qu’on a testé : Créer une nouvelle page 35
  26. REFACTOR # app/config/config_test.yml doctrine: dbal: driver: pdo_sqlite host: localhost port:

    ~ dbname: symfony user: root password: ~ path: %kernel.root_dir%/cache/database.db Créer une nouvelle page 38
  27. /** * Generates the schema to use on the test

    environment. * * @throws \Doctrine\DBAL\Schema\SchemaException */ protected static function generateSchema() { if (null === static::$kernel) { static::$kernel = static::createKernel(); } $em = static::$kernel->getContainer()->get('doctrine.orm.entity_manager'); // Get the metadata of the application to create the schema. $metadata = $em->getMetadataFactory()->getAllMetadata(); if (!empty($metadata)) { $tool = new \Doctrine\ORM\Tools\SchemaTool($em); $tool->dropDatabase(); $tool->createSchema($metadata); } else { throw new \Doctrine\DBAL\Schema\SchemaException( 'No Metadata Classes to process.' ); } } REFACTOR Créer une nouvelle page 39
  28. public function testShouldDisplayTheExpertsList() { $client = self::createClient(); self::generateSchema(); $loader =

    new \Nelmio\Alice\Loader\Base(); $objects = $loader->load($this->getExperts()); $persister = new \Nelmio\Alice\ORM\Doctrine( static::$kernel->getContainer()->get('doctrine.orm.entity_manager') ); $persister->persist($objects); $crawler = $client->request('GET', '/Experts'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan(1, $crawler->filter('li')->count()); } private function getExperts() { return array( 'Theodo\Bundle\ExpertBundle\Entity\Expert' => array( 'expert{1..5}' => array( 'firstName' => '<firstName()>', 'lastName' => '<lastName()>', 'username' => '<userName()>' ), ) ); } Créer une nouvelle page REFACTOR 40
  29. REFACTOR Créer une nouvelle page private function getExperts() { return

    array( 'Theodo\Bundle\ExpertBundle\Entity\Expert' => array( 'expert{1..5}' => array( 'firstName' => '<firstName()>', 'lastName' => '<lastName()>', 'username' => '<userName()>' ), ) ); }
  30. REFACTOR Créer une nouvelle page private function getExperts() { return

    array( 'Theodo\Bundle\ExpertBundle\Entity\Expert' => array( 'expert{1..5}' => array( 'firstName' => '<firstName()>', 'lastName' => '<lastName()>', 'username' => '<userName()>' ), ) ); } créé 5 experts
  31. REFACTOR Créer une nouvelle page private function getExperts() { return

    array( 'Theodo\Bundle\ExpertBundle\Entity\Expert' => array( 'expert{1..5}' => array( 'firstName' => '<firstName()>', 'lastName' => '<lastName()>', 'username' => '<userName()>' ), ) ); } formateurs Faker créé 5 experts
  32. REFACTOR public function testShouldDisplayTheExpertsList() { $client = self::createClient(); self::generateSchema(); $loader

    = new \Nelmio\Alice\Loader\Base(); $objects = $loader->load($this->getExperts()); $persister = new \Nelmio\Alice\ORM\Doctrine( static::$kernel->getContainer()->get('doctrine.orm.entity_manager') ); $persister->persist($objects); // ... } Créer une nouvelle page
  33. REFACTOR public function testShouldDisplayTheExpertsList() { $client = self::createClient(); self::generateSchema(); $loader

    = new \Nelmio\Alice\Loader\Base(); $objects = $loader->load($this->getExperts()); $persister = new \Nelmio\Alice\ORM\Doctrine( static::$kernel->getContainer()->get('doctrine.orm.entity_manager') ); $persister->persist($objects); // ... } Créer une nouvelle page transforme les fixtures en objets
  34. REFACTOR public function testShouldDisplayTheExpertsList() { $client = self::createClient(); self::generateSchema(); $loader

    = new \Nelmio\Alice\Loader\Base(); $objects = $loader->load($this->getExperts()); $persister = new \Nelmio\Alice\ORM\Doctrine( static::$kernel->getContainer()->get('doctrine.orm.entity_manager') ); $persister->persist($objects); // ... } Créer une nouvelle page transforme les fixtures en objets persiste les objets dans la base
  35. REFACTOR /** * @Extra\Route("/experts") * @Extra\Template() */ public function listAction()

    { $experts = $this->getDoctrine() ->getRepository('TheodoBundleExpertBundle:Expert') ->findAll() ; return array( 'experts' => $experts ); } Créer une nouvelle page 43
  36. ✔ code retour de la page = 200 ✔ la

    réponse contient une liste ✔ le nombre de li = le nombre d’experts dans la base Ce qu’on a testé : Créer une nouvelle page 45
  37. Réutiliser l’existant public function listAction() { $experts = $this->getDoctrine() ->getRepository('TheodoBundleExpertBundle:Expert')

    ->findAll() ; $expert = new Expert(); $builder = $this->createFormBuilder($Expert); $form = $builder ->add('firstName') ->add('lastName') ->add('username') ->getForm(); ; if ($this->getRequest()->isMethod('post')) { $form->bind($this->getRequest()); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($form->getData()); $em->flush(); // ici on envoie un email, au nouvel expert // on créé ses comptes sur tous les services utilisés par la boite etc... return $this->redirect($this->generateUrl('theodoexpertbundle_expert_list')); } } return array('experts' => $experts, 'form’ => $form->createView()); } 47 Contrôleur
  38. Réutiliser l’existant public function listAction() { $experts = $this->getDoctrine() ->getRepository('TheodoBundleExpertBundle:Expert')

    ->findAll() ; $expert = new Expert(); $builder = $this->createFormBuilder($Expert); $form = $builder ->add('firstName') ->add('lastName') ->add('username') ->getForm(); ; if ($this->getRequest()->isMethod('post')) { $form->bind($this->getRequest()); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($form->getData()); $em->flush(); // ici on envoie un email, au nouvel expert // on créé ses comptes sur tous les services utilisés par la boite etc... return $this->redirect($this->generateUrl('theodoexpertbundle_expert_list')); } } return array('experts' => $experts, 'form’ => $form->createView()); } 47 Contrôleur création du formulaire
  39. Réutiliser l’existant public function listAction() { $experts = $this->getDoctrine() ->getRepository('TheodoBundleExpertBundle:Expert')

    ->findAll() ; $expert = new Expert(); $builder = $this->createFormBuilder($Expert); $form = $builder ->add('firstName') ->add('lastName') ->add('username') ->getForm(); ; if ($this->getRequest()->isMethod('post')) { $form->bind($this->getRequest()); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($form->getData()); $em->flush(); // ici on envoie un email, au nouvel expert // on créé ses comptes sur tous les services utilisés par la boite etc... return $this->redirect($this->generateUrl('theodoexpertbundle_expert_list')); } } return array('experts' => $experts, 'form’ => $form->createView()); } 47 Contrôleur création du formulaire sauvegarde l’expert
  40. {% extends '::base.html.twig' %} {% block body %} <ul> {%

    for expert in experts %} <li>{{ expert }}</li> {% endfor %} </ul> <form action="{{ path('theodoexpertbundle_expert_list') }}" method="post"> {{ form_errors(form) }} {{ form_widget(form) }} <button type="submit">Save</button> </form> {% endblock %} Réutiliser l’existant 48 Template
  41. Ce qu’on va tester : • l’a!chage de la liste

    et du formulaire • la sauvegarde d’un nouvel expert test list Réutiliser l’existant 49
  42. Ce qu’on va tester : • l’a!chage de la liste

    et du formulaire • la sauvegarde d’un nouvel expert test list Réutiliser l’existant 49 • la page d’édition • la sauvegarde de l’édition d’un expert new new
  43. public function testShouldDisplayTheExpertsList() { $client = self::createClient(); self::generateSchema(); // load

    fixtures ... $crawler = $client->request('GET', '/experts'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan(1, $crawler->filter('li')->count()); $this->assertCount(1, $crawler->filter('form')); } Note : ceci est un nouveau test ! Tester l’existant 50 ✘ l’a!chage de la liste et du formulaire
  44. 53

  45. {% extends '::base.html.twig' %} {% block body %} <ul> </ul>

    {% endblock %} Tester les tests 54 ✘ l’a!chage de la liste et du formulaire
  46. {% extends '::base.html.twig' %} {% block body %} <ul> {%

    for expert in experts %} <li>{{ expert }}</li> {% endfor %} </ul> {% endblock %} Tester les tests 56 ✘ l’a!chage de la liste et du formulaire
  47. Tester les tests 58 {% extends '::base.html.twig' %} {% block

    body %} <ul> {% for expert in experts %} <li>{{ expert }}</li> {% endfor %} </ul> <form action="{{ path('theodoexpertbundle_expert_list') }}" method="post"> {{ form_errors(form) }} {{ form_widget(form) }} <button type="submit">Save</button> </form> {% endblock %} ✘ l’a!chage de la liste et du formulaire
  48. public function testShouldSaveANewExpert() { $client = self::createClient(); $csrfToken = $client->getContainer()

    ->get('form.csrf_provider') ->generateCsrfToken('unknown'); $client->request('POST', '/experts', array( 'form' => array( 'firstName' => 'Benjamin', 'lastName' => 'Grandfond', 'username' => 'benjaming', '_token' => $csrfToken, ) )); $this->assertEquals(302, $client->getResponse()->getStatusCode()); $crawler = $client->followRedirect(); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertRegExp('~Benjamin~', $crawler->filter('li')->text()); } 61 Tester l’existant ✘ sauvegarde d’un nouvel expert
  49. public function testShouldSaveANewExpert() { $client = self::createClient(); $csrfToken = $client->getContainer()

    ->get('form.csrf_provider') ->generateCsrfToken('unknown'); $client->request('POST', '/experts', array( 'form' => array( 'firstName' => 'Benjamin', 'lastName' => 'Grandfond', 'username' => 'benjaming', '_token' => $csrfToken, ) )); $this->assertEquals(302, $client->getResponse()->getStatusCode()); $crawler = $client->followRedirect(); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertRegExp('~Benjamin~', $crawler->filter('li')->text()); } 61 Tester l’existant TIP ✘ sauvegarde d’un nouvel expert
  50. namespace Theodo\Bundle\ExpertBundle\Form\Extension\Csrf\CsrfProvider; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; class MockCsrfProvider implements CsrfProviderInterface { public

    function generateCsrfToken($intention) { return $intention; } public function isCsrfTokenValid($intention, $token) { return $intention == $token; } } TIP 63 Tester l’existant
  51. Encore une fois : Ne faites pas con!ance aux tests

    écrits après le code, testez les tests ! 65
  52. ✔ l’a!chage de la liste et du formulaire ✔ la

    sauvegarde d’un nouvel expert ✘ la page d’édition ✘ la sauvegarde de l’édition d’un expert Ce qu’on a testé : test list 66 Réutiliser l’existant
  53. • validité du formulaire • retourne une redirection • retourne

    le formulaire erroné • bind le formulaire avec la request • sauvegarder l’entité du formulaire Refactorisation : Création d’un handler pour sauvegarder un expert. Ce qu’on va tester : test list Sauvegarde du formulaire 67
  54. RED namespace Theodo\Bundle\ExpertBundle\Tests\Form\Handler; use Theodo\Bundle\ExpertBundle\Form\Handler\ExpertFormHandler; class ExpertFormHandlerTest extends \PHPUnit_Framework_TestCase {

    public function testShouldHandleAnUnsuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $handler = new ExpertFormHandler(); $this->assertEquals($form, $handler->handle($form, $request)); } } Refactoriser l’existant 68 ✘ retourne le formulaire erroné
  55. 70 Un mock est un objet qui simule un autre

    objet et permet de contrôler les interactions avec l‘objet testé. • véri"er le nombre d’appels d’une méthode • spéci"er le retour de la méthode • véri"er les paramètres d’une méthode • ...
  56. RED namespace Theodo\Bundle\ExpertBundle\Form\Handler; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; class ExpertFormHandler {

    public function handle(FormInterface $form, Request $request) { return $form; } } 73 Refactoriser l’existant ✘ retourne le formulaire erroné
  57. RED namespace Theodo\Bundle\ExpertBundle\Form\Handler; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; class ExpertFormHandler {

    public function handle(FormInterface $form, Request $request) { return $form; } } 73 Refactoriser l’existant ✘ retourne le formulaire erroné code minimum !
  58. RED public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $form->expects($this->once()) ->method('isValid') ->will($this->returnValue(true)) ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request) $this->assertInstanceOf( 'Symfony\Component\HttpFoundation\RedirectResponse', $response ); $this->assertEquals('/experts', $response->getTargetUrl()); } 75 Refactoriser l’existant ✘ validité du formulaire ✘ retourne une redirection
  59. RED public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $form->expects($this->once()) ->method('isValid') ->will($this->returnValue(true)) ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request) $this->assertInstanceOf( 'Symfony\Component\HttpFoundation\RedirectResponse', $response ); $this->assertEquals('/experts', $response->getTargetUrl()); } 75 Refactoriser l’existant ✘ validité du formulaire ✘ retourne une redirection le formulaire est valide
  60. RED public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $form->expects($this->once()) ->method('isValid') ->will($this->returnValue(true)) ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request) $this->assertInstanceOf( 'Symfony\Component\HttpFoundation\RedirectResponse', $response ); $this->assertEquals('/experts', $response->getTargetUrl()); } 75 Refactoriser l’existant ✘ validité du formulaire ✘ retourne une redirection le formulaire est valide handle() retourne une redirection vers /experts
  61. RED namespace Theodo\Bundle\ExpertBundle\Form\Handler; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; class

    ExpertFormHandler { public function handle(FormInterface $form, Request $request) { if ($form->isValid()) { return new RedirectResponse("/experts"); } return $form; } } 77 Refactoriser l’existant ✘ validité du formulaire ✘ retourne une redirection
  62. REFACTOR public function testShouldHandleAnUnsuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request); // ... 79 Refactoriser l’existant public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request); // ...
  63. REFACTOR public function testShouldHandleAnUnsuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request); // ... 79 Refactoriser l’existant public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form = $this ->getMockBuilder('Symfony\Component\Form\Test\FormInterface') ->disableOriginalConstructor() ->getMock() ; $handler = new ExpertFormHandler(); $response = $handler->handle($form, $request); // ... Duplication
  64. REFACTOR class ExpertFormHandlerTest extends \PHPUnit_Framework_TestCase { protected $handler; public function

    setUp() { $this->handler = new ExpertFormHandler(); } //... 80 Refactoriser l’existant
  65. REFACTOR public function testShouldHandleAnUnsuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form

    = $this->getFromMock(); $response = $this->handler->handle($form, $request); // ... 82 Refactoriser l’existant public function testShouldHandleASuccessfulFormSubmission() { $request = new \Symfony\Component\HttpFoundation\Request(); $form = $this->getFromMock(); // ... $response = $this->handler->handle($form, $request); // ...
  66. RED namespace Theodo\Bundle\ExpertBundle\Form\Handler; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; class

    ExpertFormHandler { public function handle(FormInterface $form, Request $request) { $form->bind($request); if ($form->isValid()) { return new RedirectResponse("/experts"); } return $form; } } 86 Refactoriser l’existant ✘ bind le formulaire avec la request
  67. ✔ validité du formulaire ✔ retourne une redirection ✔ retourne

    le formulaire erroné ✔ bind le formulaire avec la request ✘ sauvegarder l’entité du formulaire Sauvegarde du formulaire test list 87
  68. ✔ validité du formulaire ✔ retourne une redirection ✔ retourne

    le formulaire erroné ✔ bind le formulaire avec la request ✘ sauvegarder l’entité du formulaire Sauvegarde du formulaire test list 87 new ✘ génère la route avec le router
  69. RED 89 Refactoriser l’existant ✘ génère la route avec le

    router class ExpertFormHandlerTest extends \PHPUnit_Framework_TestCase { protected $handler; protected $router; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface'); $this->handler = new ExpertFormHandler($this->router); } }
  70. RED 89 Refactoriser l’existant ✘ génère la route avec le

    router class ExpertFormHandlerTest extends \PHPUnit_Framework_TestCase { protected $handler; protected $router; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface'); $this->handler = new ExpertFormHandler($this->router); } } mock le router
  71. RED 89 Refactoriser l’existant ✘ génère la route avec le

    router class ExpertFormHandlerTest extends \PHPUnit_Framework_TestCase { protected $handler; protected $router; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface'); $this->handler = new ExpertFormHandler($this->router); } } mock le router injecte le router
  72. RED public function testShouldHandleASuccessfulFormSubmission() { // ... $this->router->expects($this->once()) ->method('generate') ->with($this->equalTo('theodoexpertbundle_expert_list')

    ->will($this->returnValue('/experts')) ; $response = $this->handler->handle($form, $request); // ... } 90 Refactoriser l’existant ✘ génère la route avec le router
  73. RED namespace Theodo\Bundle\ExpertBundle\Form\Handler; use ...; use Symfony\Component\Routing\RouterInterface; class ExpertFormHandler {

    /** * @var \Symfony\Component\Routing\RouterInterface */ protected $router; /** * @param RouterInterface $router */ public function __construct(RouterInterface $router) { $this->router = $router; } // ... } 92 Refactoriser l’existant ✘ génère la route avec le router
  74. RED public function handle(FormInterface $form, Request $request) { $form->bind($request); if

    ($form->isValid()) { $url = $this->router->generate( 'theodoexpertbundle_expert_list' ); return new RedirectResponse($url); } return $form; } 93 Refactoriser l’existant ✘ génère la route avec le router
  75. ✔ validité du formulaire ✔ retourne une redirection ✔ retourne

    le formulaire erroné ✔ bind le formulaire avec la request ✔ génère la route avec le router ✘ sauvegarder l’entité du formulaire Sauvegarde du formulaire 95 test list
  76. RED protected $em; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface');

    $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager') ->disableOriginalConstructor() ->getMock() ; $this->handler = new ExpertFormHandler($this->router, $this->em); } 96 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  77. RED protected $em; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface');

    $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager') ->disableOriginalConstructor() ->getMock() ; $this->handler = new ExpertFormHandler($this->router, $this->em); } 96 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire mock l’entity manager
  78. RED protected $em; public function setUp() { $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface');

    $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager') ->disableOriginalConstructor() ->getMock() ; $this->handler = new ExpertFormHandler($this->router, $this->em); } 96 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire mock l’entity manager injecte l’entity manager
  79. RED public function testShouldHandleASuccessfulFormSubmission() { // ... $expert = new

    \Theodo\Bundle\ExpertBundle\Entity\Expert(); $form->expects($this->once()) ->method('getData') ->will($this->returnValue($expert)); // ... $this->em->expects($this->once()) ->method('persist') ->with($this->identicalTo($expert)) ; $this->em->expects($this->once()) ->method('flush'); // ... 97 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  80. RED public function testShouldHandleASuccessfulFormSubmission() { // ... $expert = new

    \Theodo\Bundle\ExpertBundle\Entity\Expert(); $form->expects($this->once()) ->method('getData') ->will($this->returnValue($expert)); // ... $this->em->expects($this->once()) ->method('persist') ->with($this->identicalTo($expert)) ; $this->em->expects($this->once()) ->method('flush'); // ... 97 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire getData retournera un expert
  81. RED public function testShouldHandleASuccessfulFormSubmission() { // ... $expert = new

    \Theodo\Bundle\ExpertBundle\Entity\Expert(); $form->expects($this->once()) ->method('getData') ->will($this->returnValue($expert)); // ... $this->em->expects($this->once()) ->method('persist') ->with($this->identicalTo($expert)) ; $this->em->expects($this->once()) ->method('flush'); // ... 97 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire getData retournera un expert cet expert sera persisté
  82. RED public function testShouldHandleASuccessfulFormSubmission() { // ... $expert = new

    \Theodo\Bundle\ExpertBundle\Entity\Expert(); $form->expects($this->once()) ->method('getData') ->will($this->returnValue($expert)); // ... $this->em->expects($this->once()) ->method('persist') ->with($this->identicalTo($expert)) ; $this->em->expects($this->once()) ->method('flush'); // ... 97 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire getData retournera un expert cet expert sera persisté l’em sera "ushé
  83. RED class ExpertFormHandler { /** @var \Symfony\Component\Routing\RouterInterface */ protected $router;

    /** @var \Doctrine\ORM\EntityManager */ protected $manager; /** * @param RouterInterface $router * @param EntityManager $manager */ public function __construct(RouterInterface $router, EntityManager $manager) { $this->router = $router; $this->manager = $manager; } 99 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  84. RED class ExpertFormHandler { /** @var \Symfony\Component\Routing\RouterInterface */ protected $router;

    /** @var \Doctrine\ORM\EntityManager */ protected $manager; /** * @param RouterInterface $router * @param EntityManager $manager */ public function __construct(RouterInterface $router, EntityManager $manager) { $this->router = $router; $this->manager = $manager; } 99 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  85. RED class ExpertFormHandler { /** @var \Symfony\Component\Routing\RouterInterface */ protected $router;

    /** @var \Doctrine\ORM\EntityManager */ protected $manager; /** * @param RouterInterface $router * @param EntityManager $manager */ public function __construct(RouterInterface $router, EntityManager $manager) { $this->router = $router; $this->manager = $manager; } 99 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  86. RED class ExpertFormHandler { /** @var \Symfony\Component\Routing\RouterInterface */ protected $router;

    /** @var \Doctrine\ORM\EntityManager */ protected $manager; /** * @param RouterInterface $router * @param EntityManager $manager */ public function __construct(RouterInterface $router, EntityManager $manager) { $this->router = $router; $this->manager = $manager; } 99 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  87. RED public function handle(FormInterface $form, Request $request) { $form->bind($request); if

    ($form->isValid()) { $this->manager->persist($form->getData()); $this->manager->flush(); $route = 'theodoexpertbundle_expert_list'; return new RedirectResponse($this->router->generate($route)); } return $form; } 100 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire
  88. RED public function handle(FormInterface $form, Request $request) { $form->bind($request); if

    ($form->isValid()) { $this->manager->persist($form->getData()); $this->manager->flush(); $route = 'theodoexpertbundle_expert_list'; return new RedirectResponse($this->router->generate($route)); } return $form; } 100 Refactoriser l’existant ✘ sauvegarder l’entité du formulaire persiste l’expert puis "ush
  89. ✔ validité du formulaire ✔ retourne une redirection ✔ retourne

    le formulaire erroné ✔ bind le formulaire avec la request ✔ génère la route avec le router ✔ sauvegarder l’entité du formulaire Sauvegarde du formulaire 102 test list
  90. Conclusion • Soyez patient • Persévérez • (Ré)introduisez progressivement des

    tests • Testez ce qui est primordial • Refactorisez, corrigez, créez en TDD