{"id":2230,"date":"2013-08-15T12:58:10","date_gmt":"2013-08-15T12:58:10","guid":{"rendered":"https:\/\/intelligentbee.com\/blog\/?p=2230"},"modified":"2024-09-30T07:49:47","modified_gmt":"2024-09-30T07:49:47","slug":"symfony2-jobeet-day-9-the-functional-tests","status":"publish","type":"post","link":"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-9-the-functional-tests\/","title":{"rendered":"Symfony2 Jobeet Day 9: The Functional Tests"},"content":{"rendered":"<p><span style=\"font-family: timesnew roman; font-size: 12px;\">* This article is part of the original <a href=\"http:\/\/symfony.com\/legacy\/doc\/jobeet?orm=Doctrine\" target=\"_blank\" rel=\"noopener\">Jobeet Tutorial<\/a>, created by Fabien Potencier, for Symfony 1.4.<\/span><\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_68_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title \" >Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-9-the-functional-tests\/#Functional_tests_in_jobeet\" title=\"Functional tests in jobeet\">Functional tests in jobeet<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-9-the-functional-tests\/#Our_First_Functional_Test\" title=\"Our First Functional Test\">Our First Functional Test<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-9-the-functional-tests\/#Running_Functional_Tests\" title=\"Running Functional Tests\">Running Functional Tests<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/intelligentbee.com\/blog\/symfony2-jobeet-day-9-the-functional-tests\/#Writing_Functional_Tests\" title=\"Writing Functional Tests\">Writing Functional Tests<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Functional_tests_in_jobeet\"><\/span>Functional tests in jobeet<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Functional tests are a great tool to test your application from end to end: from the request made by a browser to the response sent by the server. They test all the layers of an application: the routing, the model, the actions and the templates. They are very similar to what you probably already do manually: each time you add or modify an action, you need to go to the browser and check that everything works as expected by clicking on links and checking elements on the rendered page. In other words, you run a scenario corresponding to the use case you have just implemented.<span id=\"more-64\"><\/span><\/p>\n<p>As the process is manual, it is tedious and error prone. Each time you change something in your code, you must step through all the scenarios to ensure that you did not break something. That\u2019s insane. Functional tests in symfony provide a way to easily describe scenarios. Each scenario can then be played automatically over and over again by simulating the experience a user has in a browser. Like unit tests, they give you the confidence to code in peace.<\/p>\n<p>Functional tests have a very specific workflow:<\/p>\n<ul>\n<li>Make a request;<\/li>\n<li>Test the response;<\/li>\n<li>Click on a link or submit a form;<\/li>\n<li>Test the response;<\/li>\n<li>Rinse and repeat;<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"Our_First_Functional_Test\"><\/span>Our First Functional Test<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Functional tests are simple PHP files that typically live in the <code>Tests\/Controller<\/code> directory of your bundle. If you want to test the pages handled by your <code>CategoryController<\/code> class, start by creating a new <code>CategoryControllerTest<\/code> class that extends a special <code>WebTestCase<\/code> class:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/CategoryControllerTest.php\">namespace IbwJobeetBundleTestsController;\r\n\r\nuse SymfonyBundleFrameworkBundleTestWebTestCase;\r\nuse SymfonyBundleFrameworkBundleConsoleApplication;\r\nuse SymfonyComponentConsoleOutputNullOutput;\r\nuse SymfonyComponentConsoleInputArrayInput;\r\nuse DoctrineBundleDoctrineBundleCommandDropDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandCreateDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandProxyCreateSchemaDoctrineCommand;\r\n\r\nclass CategoryControllerTest extends WebTestCase\r\n{\r\n    private $em;\r\n    private $application;\r\n\r\n    public function setUp()\r\n    {\r\n        static::$kernel = static::createKernel();\r\n        static::$kernel-&gt;boot();\r\n\r\n        $this-&gt;application = new Application(static::$kernel);\r\n\r\n        \/\/ drop the database\r\n        $command = new DropDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:drop',\r\n            '--force' =&gt; true\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ we have to close the connection after dropping the database so we don't get \"No database selected\" error\r\n        $connection = $this-&gt;application-&gt;getKernel()-&gt;getContainer()-&gt;get('doctrine')-&gt;getConnection();\r\n        if ($connection-&gt;isConnected()) {\r\n            $connection-&gt;close();\r\n        }\r\n\r\n        \/\/ create the database\r\n        $command = new CreateDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ create schema\r\n        $command = new CreateSchemaDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:schema:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ get the Entity Manager\r\n        $this-&gt;em = static::$kernel-&gt;getContainer()\r\n            -&gt;get('doctrine')\r\n            -&gt;getManager();\r\n\r\n        \/\/ load fixtures\r\n        $client = static::createClient();\r\n        $loader = new SymfonyBridgeDoctrineDataFixturesContainerAwareLoader($client-&gt;getContainer());\r\n        $loader-&gt;loadFromDirectory(static::$kernel-&gt;locateResource('@IbwJobeetBundle\/DataFixtures\/ORM'));\r\n        $purger = new DoctrineCommonDataFixturesPurgerORMPurger($this-&gt;em);\r\n        $executor = new DoctrineCommonDataFixturesExecutorORMExecutor($this-&gt;em, $purger);\r\n        $executor-&gt;execute($loader-&gt;getFixtures());\r\n    }\r\n\r\n    public function testShow()\r\n    {\r\n        $client = static::createClient();\r\n\r\n        $crawler = $client-&gt;request('GET', '\/category\/index');\r\n        $this-&gt;assertEquals('IbwJobeetBundleControllerCategoryController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n        $this-&gt;assertTrue(200 === $client-&gt;getResponse()-&gt;getStatusCode());\r\n    }\r\n}<\/pre>\n<p>To learn more about <code>crawler<\/code>, read the Symfony documentation <a href=\"http:\/\/symfony.com\/doc\/current\/book\/testing.html#the-crawler\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Running_Functional_Tests\"><\/span>Running Functional Tests<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>As for unit tests, launching functional tests can be done by executing the <code>phpunit<\/code> command:<\/p>\n<pre class=\"toolbar:2 nums:false lang:default decode:true \">phpunit -c app\/ src\/Ibw\/JobeetBundle\/Tests\/Controller\/CategoryControllerTest<\/pre>\n<p>This test will fail because the tested url, <code>\/category\/index<\/code>, is not a valid url in Jobeet:<\/p>\n<pre class=\"toolbar:2 lang:default decode:true \">PHPUnit 3.7.22 by Sebastian Bergmann.\r\n\r\nConfiguration read from \/var\/www\/jobeet\/app\/phpunit.xml.dist\r\n\r\nF\r\n\r\nTime: 2 seconds, Memory: 25.25Mb\r\n\r\nThere was 1 failure:\r\n\r\n1) IbwJobeetBundleTestsControllerCategoryControllerTest::testShow\r\nFailed asserting that false is true.<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Writing_Functional_Tests\"><\/span>Writing Functional Tests<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Writing functional tests is like playing a scenario in a browser. We already have written all the scenarios we need to test as part of the day 2 stories.<\/p>\n<p>First, let\u2019s test the Jobeet homepage by editing the <code>JobControllerTest<\/code> class. Replace the code with the following one:<\/p>\n<h3>EXPIRED JOBS ARE NOT LISTED<\/h3>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">namespace IbwJobeetBundleTestsController;\r\n\r\nuse SymfonyBundleFrameworkBundleTestWebTestCase;\r\nuse SymfonyBundleFrameworkBundleConsoleApplication;\r\nuse SymfonyComponentConsoleOutputNullOutput;\r\nuse SymfonyComponentConsoleInputArrayInput;\r\nuse DoctrineBundleDoctrineBundleCommandDropDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandCreateDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandProxyCreateSchemaDoctrineCommand;\r\n\r\nclass JobControllerTest extends WebTestCase\r\n{\r\n    private $em;\r\n    private $application;\r\n\r\n    public function setUp()\r\n    {\r\n        static::$kernel = static::createKernel();\r\n        static::$kernel-&gt;boot();\r\n\r\n        $this-&gt;application = new Application(static::$kernel);\r\n\r\n        \/\/ drop the database\r\n        $command = new DropDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:drop',\r\n            '--force' =&gt; true\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ we have to close the connection after dropping the database so we don't get \"No database selected\" error\r\n        $connection = $this-&gt;application-&gt;getKernel()-&gt;getContainer()-&gt;get('doctrine')-&gt;getConnection();\r\n        if ($connection-&gt;isConnected()) {\r\n            $connection-&gt;close();\r\n        }\r\n\r\n        \/\/ create the database\r\n        $command = new CreateDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ create schema\r\n        $command = new CreateSchemaDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:schema:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ get the Entity Manager\r\n        $this-&gt;em = static::$kernel-&gt;getContainer()\r\n            -&gt;get('doctrine')\r\n            -&gt;getManager();\r\n\r\n        \/\/ load fixtures\r\n        $client = static::createClient();\r\n        $loader = new SymfonyBridgeDoctrineDataFixturesContainerAwareLoader($client-&gt;getContainer());\r\n        $loader-&gt;loadFromDirectory(static::$kernel-&gt;locateResource('@IbwJobeetBundle\/DataFixtures\/ORM'));\r\n        $purger = new DoctrineCommonDataFixturesPurgerORMPurger($this-&gt;em);\r\n        $executor = new DoctrineCommonDataFixturesExecutorORMExecutor($this-&gt;em, $purger);\r\n        $executor-&gt;execute($loader-&gt;getFixtures());\r\n    }\r\n\r\n    public function testIndex()\r\n    {\r\n        $client = static::createClient();\r\n        $crawler = $client-&gt;request('GET', '\/');\r\n\r\n        $this-&gt;assertEquals('IbwJobeetBundleControllerJobController::indexAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n        $this-&gt;assertTrue($crawler-&gt;filter('.jobs td.position:contains(\"Expired\")')-&gt;count() == 0);\r\n    }\r\n}<\/pre>\n<p>To verify the exclusion of expired jobs from the homepage, we check that the CSS selector <code>.jobs td.position:contains(\"Expired\")<\/code> does not match anywhere in the response HTML content (remember that in the fixtures, the only expired job we have contains \u201cExpired\u201d in the position).<\/p>\n<h3>ONLY N JOBS ARE LISTED FOR A CATEGORY<\/h3>\n<p>Add the following code at the end of your <code>testIndex()<\/code> function. To get the custom parameter defined in <code>app\/config\/config.yml<\/code> in our functional test, we will use the kernel:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">public function testIndex()\r\n{\r\n    \/\/...\r\n    $kernel = static::createKernel();\r\n    $kernel-&gt;boot();\r\n    $max_jobs_on_homepage = $kernel-&gt;getContainer()-&gt;getParameter('max_jobs_on_homepage');\r\n    $this-&gt;assertTrue($crawler-&gt;filter('.category_programming tr')-&gt;count() &lt;= $max_jobs_on_homepage );\r\n}<\/pre>\n<p>For this test to work we will need to add the corresponding CSS class to each category in the <code>Job\/index.html.twig<\/code> file (so we can select each category and count the jobs listed) :<\/p>\n<pre class=\"lang:xhtml decode:true \" title=\"src\/Ibw\/JobeetBundle\/Resources\/views\/Job\/index.html.twig\">&lt;!-- ... --&gt;\r\n\r\n    {% for category in categories %}\r\n        &lt;div class=\"category_{{ category.slug }}\"&gt;\r\n           &lt;div class=\"category\"&gt;\r\n&lt;!-- ... --&gt;<\/pre>\n<h3>A CATEGORY HAS A LINK TO THE CATEGORY PAGE ONLY IF TOO MANY JOBS<\/h3>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">public function testIndex()\r\n{\r\n    \/\/...\r\n    $this-&gt;assertTrue($crawler-&gt;filter('.category_design .more_jobs')-&gt;count() == 0);\r\n    $this-&gt;assertTrue($crawler-&gt;filter('.category_programming .more_jobs')-&gt;count() == 1);\r\n}<\/pre>\n<p>In these tests, we check that there is no \u201cmore jobs\u201d link for the design category <code>(.category_design .more_jobs<\/code> does not exist), and that there is a \u201cmore jobs\u201d link for the programming category <code>(<code>.category_programming .more_jobs does exist<\/code>).<\/code><\/p>\n<h3>JOBS ARE SORTED BY DATE<\/h3>\n<p>To test if jobs are actually sorted by date, we need to check that the first job listed on the homepage is the one we expect. This can be done by checking that the URL contains the expected primary key. As the primary key can change between runs, we need to get the Doctrine object from the database first.<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">public function testIndex()\r\n{    \r\n    \/\/ ...\r\n    $em = $kernel-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');\r\n\r\n    $query = $em-&gt;createQuery('SELECT j from IbwJobeetBundle:Job j LEFT JOIN j.category c WHERE c.slug = :slug AND j.expires_at &gt; :date ORDER BY j.created_at DESC');\r\n    $query-&gt;setParameter('slug', 'programming');\r\n    $query-&gt;setParameter('date', date('Y-m-d H:i:s', time()));\r\n    $query-&gt;setMaxResults(1);\r\n    $job = $query-&gt;getSingleResult();\r\n\r\n    $this-&gt;assertTrue($crawler-&gt;filter('.category_programming tr')-&gt;first()-&gt;filter(sprintf('a[href*=\"\/%d\/\"]', $job-&gt;getId()))-&gt;count() == 1);\r\n}<\/pre>\n<p>Even if the test works in this very moment, we need to refactor the code a bit, as getting the first job of the programming category can be reused elsewhere in our tests. We won\u2019t move the code to the Model layer as the code is test specific. Instead, we will move the code to the <code>getMostRecentProgrammingJob<\/code> function in our test class:<\/p>\n<pre class=\"lang:php decode:true\" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">\/\/ ...\r\n\r\n    public function getMostRecentProgrammingJob()\r\n    {\r\n        $kernel = static::createKernel();\r\n        $kernel-&gt;boot();\r\n        $em = $kernel-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');\r\n\r\n        $query = $em-&gt;createQuery('SELECT j from IbwJobeetBundle:Job j LEFT JOIN j.category c WHERE c.slug = :slug AND j.expires_at &gt; :date ORDER BY j.created_at DESC');\r\n        $query-&gt;setParameter('slug', 'programming');\r\n        $query-&gt;setParameter('date', date('Y-m-d H:i:s', time()));\r\n        $query-&gt;setMaxResults(1);\r\n\r\n        return $query-&gt;getSingleResult();\r\n    }\r\n\r\n\/\/ ...<\/pre>\n<p>You can now replace the previous test code by the following one:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">\/\/ ...\r\n\r\n$this-&gt;assertTrue($crawler-&gt;filter('.category_programming tr')-&gt;first()-&gt;filter(sprintf('a[href*=\"\/%d\/\"]', $this-&gt;getMostRecentProgrammingJob()-&gt;getId()))-&gt;count() == 1);\r\n\r\n\/\/...<\/pre>\n<h3>EACH JOB ON THE HOMEPAGE IS CLICKABLE<\/h3>\n<p>To test the job link on the homepage, we simulate a click on the \u201cWeb Developer\u201d text. As there are many of them on the page, we have explicitly to ask the browser to click on the first one.<\/p>\n<p>Each request parameter is then tested to ensure that the routing has done its job correctly.<\/p>\n<pre class=\"lang:php decode:true\" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">public function testIndex() \r\n{\r\n    \/\/ ...\r\n\r\n    $job = $this-&gt;getMostRecentProgrammingJob();\r\n    $link = $crawler-&gt;selectLink('Web Developer')-&gt;first()-&gt;link();\r\n    $crawler = $client-&gt;click($link);\r\n    $this-&gt;assertEquals('IbwJobeetBundleControllerJobController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n    $this-&gt;assertEquals($job-&gt;getCompanySlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('company'));\r\n    $this-&gt;assertEquals($job-&gt;getLocationSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('location'));\r\n    $this-&gt;assertEquals($job-&gt;getPositionSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('position'));\r\n    $this-&gt;assertEquals($job-&gt;getId(), $client-&gt;getRequest()-&gt;attributes-&gt;get('id'));\r\n}\r\n\r\n\/\/ ...<\/pre>\n<h3>LEARN BY THE EXAMPLE<\/h3>\n<p>In this section, you have all the code needed to test the job and category pages. Read the code carefully as you may learn some new neat tricks:<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/JobControllerTest.php\">namespace IbwJobeetBundleTestsController;\r\n\r\nuse SymfonyBundleFrameworkBundleTestWebTestCase;\r\nuse SymfonyBundleFrameworkBundleConsoleApplication;\r\nuse SymfonyComponentConsoleOutputNullOutput;\r\nuse SymfonyComponentConsoleInputArrayInput;\r\nuse DoctrineBundleDoctrineBundleCommandDropDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandCreateDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandProxyCreateSchemaDoctrineCommand;\r\n\r\nclass JobControllerTest extends WebTestCase\r\n{\r\n    private $em;\r\n    private $application;\r\n\r\n    public function setUp()\r\n    {\r\n        static::$kernel = static::createKernel();\r\n        static::$kernel-&gt;boot();\r\n\r\n        $this-&gt;application = new Application(static::$kernel);\r\n\r\n        \/\/ drop the database\r\n        $command = new DropDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:drop',\r\n            '--force' =&gt; true\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ we have to close the connection after dropping the database so we don't get \"No database selected\" error\r\n        $connection = $this-&gt;application-&gt;getKernel()-&gt;getContainer()-&gt;get('doctrine')-&gt;getConnection();\r\n        if ($connection-&gt;isConnected()) {\r\n            $connection-&gt;close();\r\n        }\r\n\r\n        \/\/ create the database\r\n        $command = new CreateDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ create schema\r\n        $command = new CreateSchemaDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:schema:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ get the Entity Manager\r\n        $this-&gt;em = static::$kernel-&gt;getContainer()\r\n            -&gt;get('doctrine')\r\n            -&gt;getManager();\r\n\r\n        \/\/ load fixtures\r\n        $client = static::createClient();\r\n        $loader = new SymfonyBridgeDoctrineDataFixturesContainerAwareLoader($client-&gt;getContainer());\r\n        $loader-&gt;loadFromDirectory(static::$kernel-&gt;locateResource('@IbwJobeetBundle\/DataFixtures\/ORM'));\r\n        $purger = new DoctrineCommonDataFixturesPurgerORMPurger($this-&gt;em);\r\n        $executor = new DoctrineCommonDataFixturesExecutorORMExecutor($this-&gt;em, $purger);\r\n        $executor-&gt;execute($loader-&gt;getFixtures());\r\n    }\r\n\r\n    public function getMostRecentProgrammingJob()\r\n    {\r\n        $kernel = static::createKernel();\r\n        $kernel-&gt;boot();\r\n        $em = $kernel-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');\r\n\r\n        $query = $em-&gt;createQuery('SELECT j from IbwJobeetBundle:Job j LEFT JOIN j.category c WHERE c.slug = :slug AND j.expires_at &gt; :date ORDER BY j.created_at DESC');\r\n        $query-&gt;setParameter('slug', 'programming');\r\n        $query-&gt;setParameter('date', date('Y-m-d H:i:s', time()));\r\n        $query-&gt;setMaxResults(1);\r\n\r\n        return $query-&gt;getSingleResult();\r\n    }\r\n\r\n    public function getExpiredJob()\r\n    {\r\n        $kernel = static::createKernel();\r\n        $kernel-&gt;boot();\r\n        $em = $kernel-&gt;getContainer()-&gt;get('doctrine.orm.entity_manager');\r\n\r\n        $query = $em-&gt;createQuery('SELECT j from IbwJobeetBundle:Job j WHERE j.expires_at &lt; :date');             \r\n        $query-&gt;setParameter('date', date('Y-m-d H:i:s', time()));\r\n        $query-&gt;setMaxResults(1);\r\n\r\n        return $query-&gt;getSingleResult();\r\n    }\r\n\r\n    public function testIndex()\r\n    {\r\n        \/\/ get the custom parameters from app config.yml\r\n        $kernel = static::createKernel();\r\n        $kernel-&gt;boot();\r\n        $max_jobs_on_homepage = $kernel-&gt;getContainer()-&gt;getParameter('max_jobs_on_homepage');\r\n\r\n        $client = static::createClient();\r\n\r\n        $crawler = $client-&gt;request('GET', '\/');\r\n        $this-&gt;assertEquals('IbwJobeetBundleControllerJobController::indexAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n\r\n        \/\/ expired jobs are not listed\r\n        $this-&gt;assertTrue($crawler-&gt;filter('.jobs td.position:contains(\"Expired\")')-&gt;count() == 0);\r\n\r\n        \/\/ only $max_jobs_on_homepage jobs are listed for a category\r\n        $this-&gt;assertTrue($crawler-&gt;filter('.category_programming tr')-&gt;count()&lt;= $max_jobs_on_homepage); \r\n        $this-&gt;assertTrue($crawler-&gt;filter('.category_design .more_jobs')-&gt;count() == 0);\r\n        $this-&gt;assertTrue($crawler-&gt;filter('.category_programming .more_jobs')-&gt;count() == 1);\r\n\r\n        \/\/ jobs are sorted by date\r\n        $this-&gt;assertTrue($crawler-&gt;filter('.category_programming tr')-&gt;first()-&gt;filter(sprintf('a[href*=\"\/%d\/\"]', $this-&gt;getMostRecentProgrammingJob()-&gt;getId()))-&gt;count() == 1);\r\n\r\n        \/\/ each job on the homepage is clickable and give detailed information\r\n        $job = $this-&gt;getMostRecentProgrammingJob();\r\n        $link = $crawler-&gt;selectLink('Web Developer')-&gt;first()-&gt;link();\r\n        $crawler = $client-&gt;click($link);\r\n        $this-&gt;assertEquals('IbwJobeetBundleControllerJobController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n        $this-&gt;assertEquals($job-&gt;getCompanySlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('company'));\r\n        $this-&gt;assertEquals($job-&gt;getLocationSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('location'));\r\n        $this-&gt;assertEquals($job-&gt;getPositionSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('position'));\r\n        $this-&gt;assertEquals($job-&gt;getId(), $client-&gt;getRequest()-&gt;attributes-&gt;get('id'));\r\n\r\n        \/\/ a non-existent job forwards the user to a 404\r\n        $crawler = $client-&gt;request('GET', '\/job\/foo-inc\/milano-italy\/0\/painter');\r\n        $this-&gt;assertTrue(404 === $client-&gt;getResponse()-&gt;getStatusCode());\r\n\r\n        \/\/ an expired job page forwards the user to a 404\r\n        $crawler = $client-&gt;request('GET', sprintf('\/job\/sensio-labs\/paris-france\/%d\/web-developer', $this-&gt;getExpiredJob()-&gt;getId()));\r\n        $this-&gt;assertTrue(404 === $client-&gt;getResponse()-&gt;getStatusCode());\r\n    }\r\n}<\/pre>\n<p>&nbsp;<\/p>\n<pre class=\"lang:php decode:true \" title=\"src\/Ibw\/JobeetBundle\/Tests\/Controller\/CategoryControllerTest.php\">namespace IbwJobeetBundleTestsController;\r\n\r\nuse SymfonyBundleFrameworkBundleTestWebTestCase;\r\nuse SymfonyBundleFrameworkBundleConsoleApplication;\r\nuse SymfonyComponentConsoleOutputNullOutput;\r\nuse SymfonyComponentConsoleInputArrayInput;\r\nuse DoctrineBundleDoctrineBundleCommandDropDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandCreateDatabaseDoctrineCommand;\r\nuse DoctrineBundleDoctrineBundleCommandProxyCreateSchemaDoctrineCommand;\r\n\r\nclass CategoryControllerTest extends WebTestCase\r\n{\r\n    private $em;\r\n    private $application;\r\n    public function setUp()\r\n    {\r\n        static::$kernel = static::createKernel();\r\n        static::$kernel-&gt;boot();\r\n\r\n        $this-&gt;application = new Application(static::$kernel);\r\n\r\n        \/\/ drop the database\r\n        $command = new DropDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:drop',\r\n            '--force' =&gt; true\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ we have to close the connection after dropping the database so we don't get \"No database selected\" error\r\n        $connection = $this-&gt;application-&gt;getKernel()-&gt;getContainer()-&gt;get('doctrine')-&gt;getConnection();\r\n        if ($connection-&gt;isConnected()) {\r\n            $connection-&gt;close();\r\n        }\r\n\r\n        \/\/ create the database\r\n        $command = new CreateDatabaseDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:database:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ create schema\r\n        $command = new CreateSchemaDoctrineCommand();\r\n        $this-&gt;application-&gt;add($command);\r\n        $input = new ArrayInput(array(\r\n            'command' =&gt; 'doctrine:schema:create',\r\n        ));\r\n        $command-&gt;run($input, new NullOutput());\r\n\r\n        \/\/ get the Entity Manager\r\n        $this-&gt;em = static::$kernel-&gt;getContainer()\r\n            -&gt;get('doctrine')\r\n            -&gt;getManager();\r\n\r\n        \/\/ load fixtures\r\n        $client = static::createClient();\r\n        $loader = new SymfonyBridgeDoctrineDataFixturesContainerAwareLoader($client-&gt;getContainer());\r\n        $loader-&gt;loadFromDirectory(static::$kernel-&gt;locateResource('@IbwJobeetBundle\/DataFixtures\/ORM'));\r\n        $purger = new DoctrineCommonDataFixturesPurgerORMPurger($this-&gt;em);\r\n        $executor = new DoctrineCommonDataFixturesExecutorORMExecutor($this-&gt;em, $purger);\r\n        $executor-&gt;execute($loader-&gt;getFixtures());\r\n    }\r\n\r\n    public function testShow()\r\n    {\r\n        $kernel = static::createKernel();\r\n        $kernel-&gt;boot();\r\n\r\n        \/\/ get the custom parameters from app\/config.yml\r\n        $max_jobs_on_category = $kernel-&gt;getContainer()-&gt;getParameter('max_jobs_on_category');\r\n        $max_jobs_on_homepage = $kernel-&gt;getContainer()-&gt;getParameter('max_jobs_on_homepage');\r\n\r\n        $client = static::createClient();\r\n\r\n        $categories = $this-&gt;em-&gt;getRepository('IbwJobeetBundle:Category')-&gt;getWithJobs();\r\n\r\n        \/\/ categories on homepage are clickable\r\n        foreach($categories as $category) {\r\n            $crawler = $client-&gt;request('GET', '\/');\r\n\r\n            $link = $crawler-&gt;selectLink($category-&gt;getName())-&gt;link();\r\n            $crawler = $client-&gt;click($link);\r\n\r\n            $this-&gt;assertEquals('IbwJobeetBundleControllerCategoryController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n            $this-&gt;assertEquals($category-&gt;getSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('slug'));\r\n\r\n            $jobs_no = $this-&gt;em-&gt;getRepository('IbwJobeetBundle:Job')-&gt;countActiveJobs($category-&gt;getId()); \r\n\r\n            \/\/ categories with more than $max_jobs_on_homepage jobs also have a \"more\" link                 \r\n            if($jobs_no &gt; $max_jobs_on_homepage) {\r\n                $crawler = $client-&gt;request('GET', '\/');\r\n                $link = $crawler-&gt;filter(\".category_\" . $category-&gt;getSlug() . \" .more_jobs a\")-&gt;link();\r\n                $crawler = $client-&gt;click($link);\r\n\r\n                $this-&gt;assertEquals('IbwJobeetBundleControllerCategoryController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n                $this-&gt;assertEquals($category-&gt;getSlug(), $client-&gt;getRequest()-&gt;attributes-&gt;get('slug'));\r\n            }\r\n\r\n            $pages = ceil($jobs_no\/$max_jobs_on_category);\r\n\r\n            \/\/ only $max_jobs_on_category jobs are listed \r\n            $this-&gt;assertTrue($crawler-&gt;filter('.jobs tr')-&gt;count() &lt;= $max_jobs_on_category);\r\n            $this-&gt;assertRegExp(\"\/\" . $jobs_no . \" jobs\/\", $crawler-&gt;filter('.pagination_desc')-&gt;text());\r\n\r\n            if($pages &gt; 1) {\r\n                $this-&gt;assertRegExp(\"\/page 1\/\" . $pages . \"\/\", $crawler-&gt;filter('.pagination_desc')-&gt;text());\r\n\r\n                for ($i = 2; $i &lt;= $pages; $i++) {\r\n                    $link = $crawler-&gt;selectLink($i)-&gt;link();\r\n                    $crawler = $client-&gt;click($link);\r\n\r\n                    $this-&gt;assertEquals('IbwJobeetBundleControllerCategoryController::showAction', $client-&gt;getRequest()-&gt;attributes-&gt;get('_controller'));\r\n                    $this-&gt;assertEquals($i, $client-&gt;getRequest()-&gt;attributes-&gt;get('page'));\r\n                    $this-&gt;assertTrue($crawler-&gt;filter('.jobs tr')-&gt;count() &lt;= $max_jobs_on_category);\r\n                    if($jobs_no &gt;1) {\r\n                        $this-&gt;assertRegExp(\"\/\" . $jobs_no . \" jobs\/\", $crawler-&gt;filter('.pagination_desc')-&gt;text());\r\n                    }\r\n                    $this-&gt;assertRegExp(\"\/page \" . $i . \"\/\" . $pages . \"\/\", $crawler-&gt;filter('.pagination_desc')-&gt;text());\r\n                }\r\n            }     \r\n        }\r\n    }\r\n}<\/pre>\n<h4>That\u2019s all for today! Tomorrow, we will learn all there is to know about forms.<\/h4>\n<p><a href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\" rel=\"license\"><img decoding=\"async\" style=\"border-width: 0;\" src=\"\/\/i.creativecommons.org\/l\/by-sa\/3.0\/88x31.png\" alt=\"Creative Commons License\" \/><\/a><br \/>\nThis work is licensed under a <a href=\"http:\/\/creativecommons.org\/licenses\/by-sa\/3.0\/\" target=\"_blank\" rel=\"license noopener\">Creative Commons Attribution-ShareAlike 3.0 Unported License<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>* This article is part of the original Jobeet Tutorial, created by Fabien Potencier, for Symfony 1.4. Functional tests in [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[82],"tags":[],"yst_prominent_words":[394,528,1355,1361,1556,1982],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2230"}],"collection":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/comments?post=2230"}],"version-history":[{"count":4,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2230\/revisions"}],"predecessor-version":[{"id":133238,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/posts\/2230\/revisions\/133238"}],"wp:attachment":[{"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/media?parent=2230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/categories?post=2230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/tags?post=2230"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/intelligentbee.com\/blog\/wp-json\/wp\/v2\/yst_prominent_words?post=2230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}