Unit Tests in Symfony2

Unit Tests in Symfony2

A talk I made on SymfonyPot Paris May 2013. Symfony2 Test Culture, how to write unit tests etc. Thanks to @benjaM1 for the nice Theodo design.

908a0c79fc96a06684fa7e2a523bde1b?s=128

Marek Kalnik

May 14, 2013
Tweet

Transcript

  1. Unit Tests Unit Tests in Symfony2 in Symfony2 Marek Kalnik

    Marek Kalnik
  2. 2 Symfony is one of the few Symfony is one

    of the few PHP projects (...) promoting PHP projects (...) promoting radical changes to the way radical changes to the way developers work developers work Fabien Potencier Fabien Potencier
  3. 3 <?php include 'includes/opendb.php'; include 'includes/header.inc'; ?> <h1>Orders</h1> <table> <?php

    $sql = “SELECT * FROM orders WHERE client_id = $GET['client_id']“; $result = mysql_query($sql); if ($result) { while ($row = mysql_fetch_row($result)) { echo “<tr><td>$row['label']</td><td>$row['value']</td><tr>”; } } ?> </table> <?php include 'includes/closedb.php'; include 'includes/footer.inc'; ?> <?php include 'includes/opendb.php'; include 'includes/header.inc'; ?> <h1>Orders</h1> <table> <?php $sql = “SELECT * FROM orders WHERE client_id = $GET['client_id']“; $result = mysql_query($sql); if ($result) { while ($row = mysql_fetch_row($result)) { echo “<tr><td>$row['label']</td><td>$row['value']</td><tr>”; } } ?> </table> <?php include 'includes/closedb.php'; include 'includes/footer.inc'; ?> PHP began with code like that (me too) PHP began with code like that (me too)
  4. 4 <?php include 'includes/opendb.php'; include 'includes/header.inc'; ?> <h1>Orders</h1> <table> <?php

    $sql = “SELECT * FROM orders WHERE client_id = $GET['client_id']“; $result = mysql_query($sql); if ($result) { while ($row = mysql_fetch_row($result)) { echo “<tr><td>$row['label']</td><td>$row['value']</td><tr>”; } } ?> </table> <?php include 'includes/closedb.php'; include 'includes/footer.inc'; ?> <?php include 'includes/opendb.php'; include 'includes/header.inc'; ?> <h1>Orders</h1> <table> <?php $sql = “SELECT * FROM orders WHERE client_id = $GET['client_id']“; $result = mysql_query($sql); if ($result) { while ($row = mysql_fetch_row($result)) { echo “<tr><td>$row['label']</td><td>$row['value']</td><tr>”; } } ?> </table> <?php include 'includes/closedb.php'; include 'includes/footer.inc'; ?> We want to improve our work We want to improve our work DIFFICULT TO REUSE SECURITY RISK NO SEPARATION OF CONCERNS NOT TESTED!
  5. 5 We have to participate in the We have to

    participate in the changes introduced by Symfony changes introduced by Symfony
  6. 6 We should unit test our We should unit test

    our applications applications
  7. Unit tests Unit tests it is not only testing it

    is not only testing
  8. 8 Impact on development Impact on development According to university

    study by David S. Janzen[1]: According to university study by David S. Janzen[1]: • reduces the number of lines of code reduces the number of lines of code • improves productivity improves productivity • less effort per line-of-code less effort per line-of-code • improves code structure improves code structure • makes developer more confident about their work makes developer more confident about their work [1] http://src.acm.org/subpages/gf_entries_06/DavidJanzen_src_gf06.pdf
  9. 9 TDD – even better TDD – even better Further

    improves the code stability and quality: Further improves the code stability and quality: • http://research.microsoft.com/en-us/groups/ese/nagappan_tdd.pdf http://research.microsoft.com/en-us/groups/ese/nagappan_tdd.pdf • http://research.microsoft.com/en-us/groups/ese/fp17288-bhat.pdf http://research.microsoft.com/en-us/groups/ese/fp17288-bhat.pdf • and others... and others... Most successful Theodo Most successful Theodo projects were written using projects were written using the TDD technique. the TDD technique.
  10. Symfony2 Symfony2 Test Culture Test Culture

  11. 11 Symfony2 tests Symfony2 tests • early versions ~5100 tests

    early versions ~5100 tests • today ~9000 tests today ~9000 tests • Documentation! Documentation! • http://symfony.com/doc/current/book/testing.html http://symfony.com/doc/current/book/testing.html • http://symfony.com/doc/current/cookbook/testing/index.html http://symfony.com/doc/current/cookbook/testing/index.html • http://symfony.com/doc/current/cookbook/form/unit_testing.html http://symfony.com/doc/current/cookbook/form/unit_testing.html
  12. 12 Interesting TestCases Interesting TestCases • FormComponent::FormIntegrationTestCase FormComponent::FormIntegrationTestCase • FormComponent::TypeTestCase

    FormComponent::TypeTestCase • *PerformanceTestCase *PerformanceTestCase • LocaleComponent::TestCase LocaleComponent::TestCase • FosUserBundle::FosUserExtensionTest FosUserBundle::FosUserExtensionTest • getFullConfig, getEmptyConfig getFullConfig, getEmptyConfig   • TheodoRogerCmsBundle ;-) TheodoRogerCmsBundle ;-)
  13. 13 Read The Read The Docs Docs Tests! Tests!

  14. So how do you do it? So how do you

    do it?
  15. 15 Tools Tools PHPUnit running and writing the tests because

    it's the industry standard Phake (https://github.com/mlively/Phake) mocking because PHPUnit mocks are not flexible enough
  16. 16 Install Install // composer.json require-dev: { “phpunit/phpunit”: “*”, “phake/phake”:

    “*” } // composer.json require-dev: { “phpunit/phpunit”: “*”, “phake/phake”: “*” }
  17. 17 tests.bootstrap.php tests.bootstrap.php <?php require_once __DIR__ . '/bootstrap.php.cache'; Phake::setClient(Phake::CLIENT_PHPUNIT); <?php

    require_once __DIR__ . '/bootstrap.php.cache'; Phake::setClient(Phake::CLIENT_PHPUNIT); add it in phpunit.xml add it in phpunit.xml
  18. 18 the setUp the setUp <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension;

    use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } } <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } }
  19. 19 the mock the mock <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension;

    use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); } } <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); } }
  20. 20 the test the test <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension;

    use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); $this->assertTrue($this->enabler->isEnabled()); Phake::verify($this->securityContext, Phake::times(1))->isGranted('ROLE_ADMIN', null); } } <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); $this->assertTrue($this->enabler->isEnabled()); Phake::verify($this->securityContext, Phake::times(1))->isGranted('ROLE_ADMIN', null); } }
  21. 21 and more and more <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension;

    use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); $this->enabler->setEnv('cms'); $this->assertTrue($this->enabler->isEnabled()); $this->enabler->setEnv('prod'); $this->assertFalse($this->enabler->isEnabled()); Phake::verify($this->securityContext, Phake::times(1))->isGranted('ROLE_ADMIN', null); } } <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake; class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } public function testChecksIfCmsShouldBeEnabled() { Phake::when($this->securityContext)->isGranted('ROLE_ADMIN', null) ->thenReturn(true); $this->enabler->setEnv('cms'); $this->assertTrue($this->enabler->isEnabled()); $this->enabler->setEnv('prod'); $this->assertFalse($this->enabler->isEnabled()); Phake::verify($this->securityContext, Phake::times(1))->isGranted('ROLE_ADMIN', null); } }
  22. 22 group group <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake;

    /** * @group unit */ class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } <?php namespace Theodo\CmsBundle\Tests\Twig; use Theodo\CmsBundle\Twig\CmsEnablerExtension; use Phake; /** * @group unit */ class CmsEnablerExtensionTest extends \PHPUnit_Framework_TestCase { public function setUp() { $this->securityContext = Phake::mock('Symfony\Component\Security\Core\SecurityContextInterface'); $this->enabler = new CmsEnablerExtension($this->securityContext); } $ bin/phpunit -c app/ --group unit $ bin/phpunit -c app/ --group unit
  23. 23 other stuff other stuff refactor your tests – it's

    a code to maintain too! use fixtures (Alice, Phaker) ! be close to production
  24. Questions ? Questions ?