📜 ⬆️ ⬇️

Creating a blog on symfony 2.8 lts [Part 6]






Project on Github
You can find out how to install the part of the manual you need in the description of the repository by the link . (For example, if you want to start with this lesson without going through the previous one)

')
Testing in symfony2

PHPUnit has become the “de facto standard” for writing tests in PHP, so learning it will benefit you in all your PHP projects. Let us also not forget that most of the topics covered in this section are language independent, and therefore can be applied to other languages ​​that you use.

The board

If you plan to write your own Open Source Symfony2 bundles, you are much more likely to get a good response from users if your bundle is well tested (and documented). Look at the list of existing symfony2 bundles available on knpbundles .


Unit testing


Unit testing verifies the proper operation of individual function units when used in isolation. In object-oriented code like in symfony2, a module is a class and its methods. For example, we could write tests for Blog and Comment entity classes. When writing unit tests, the tests should be written independently of other tests, that is, the result of test B should not depend on the result of A. Unit testing allows you to create fictitious objects that allow you to test functions that have external dependencies. Dummy objects (mocks) allow you to simulate a function call instead of the actual execution. An example of this might be unit testing of a class that wraps an external API. The API class can use the transport layer protocol to communicate with an external API. We could make a dummy transport-level query method to return the results that we set, rather than actually returning from the external API. Unit testing does not verify if the components of an application function correctly together, as shown in the next section.

Functional testing


Functional testing verifies the integration of various application components, such as routing, controllers, and mappings. Functional tests are similar to manual tests that you could do in your browser, for example, requesting the main page, clicking on the record link and checking that it was displayed correctly. Functional testing gives you the ability to automate this process. Symfony2 comes bundled with a variety of useful classes that help with functional testing, including the Client , which is able to make requests for pages, submit forms and a DOM Crawler that we can use to bypass the Response from the client.

PHPUnit


As mentioned above, symfony2 tests are written using PHPUnit. You will need to install PHPUnit in order to run tests from this part. For more installation information, refer to the official documentation on the PHPUnit website . To run tests in Symfony2, you need to install PHPUnit 5.4 (requires PHP 5.6). PHPUnit is a very large library of testing, in connection with this reference will be made to the official documentation, where additional information may be required.

Assertions


Writing tests is a test of whether the expected test result is equal to the actual result. There are a number of assertion methods in PHPUnit to help us with this task. Some of the common assertion methods that we will use are listed below.

// Check 1 === 1 is true $this->assertTrue(1 === 1); // Check 1 === 2 is false $this->assertFalse(1 === 2); // Check 'Hello' equals 'Hello' $this->assertEquals('Hello', 'Hello'); // Check array has key 'language' $this->assertArrayHasKey('language', array('language' => 'php', 'size' => '1024')); // Check array contains value 'php' $this->assertContains('php', array('php', 'ruby', 'c++', 'JavaScript')); 


A complete list of assertions is available in the PHPUnit documentation.

Running symfony2 tests


Before we start writing a few tests, let's take a look at how we run the tests in Symfony2. PHPUnit can use the configuration file. In our Symfony2 project, this file is app / phpunit.xml.dist . Since this file is suffixed with .dist , you need to copy its contents to a file called app / phpunit.xml .

The board

If you are using VCS, such as Git, you must add the app / phpunit.xml file to the ignore list.


If you look at the contents of the PHPUnit configuration file, you will see the following.

 <!-- app/phpunit.xml --> <testsuites> <testsuite name="Project Test Suite"> <directory>../src/*/*Bundle/Tests</directory> <directory>../src/*/Bundle/*Bundle/Tests</directory> </testsuite> </testsuites> 


These parameters set up some directories that are part of our test suite. When running, PHPUnit will look for tests to run in the above directories. You can also pass additional arguments from the command line to run PHPUnit tests in certain directories. You will see how to achieve this later.

The board

For more information on configuring PHPUnit using an XML file, see the PHPUnit documentation.


Run current tests


Since we used one of the Symfony2 generator commands to create the Blogger BlogBundle in the first part, she also created a controller test for the Default Controller class. We can perform this test by running the following command from the project root directory. The -c option indicates that PHPUnit should load its configuration from the app directory.
 $ phpunit -c app 


After testing completes, you should be notified that the test failed. If you look at the class DefaultControllerTest located src / Blogger / BlogBundle / Tests / Controller / DefaultControllerTest.php you will see
following content
 <?php namespace Blogger\BlogBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class DefaultControllerTest extends WebTestCase { public function testIndex() { $client = static::createClient(); $crawler = $client->request('GET', '/'); $this->assertContains('Hello World', $client->getResponse()->getContent()); } } 



This is a functional test for the Default Controller class that Symfony2 generated. If you remember in Part 1, this controller had a method that processed the request /, at this address we displayed a template with the content “Hello World” . Since we have deleted this controller and the template has completely different content along this route, the above test fails.
Functional testing is a big part of this chapter and will be discussed in detail later.
Since the Default Controller class has been deleted, you can delete the src / Blogger / BlogBundle / Tests / Controller / DefaultControllerTest.php file

Unit testing


As explained earlier, unit testing deals with testing individual units of your application in isolation. When writing unit tests, it is recommended to duplicate the bundle structure in the tests folder. For example, if you want to test the Blog class entity located in src / Blogger / BlogBundle / Entity / Blog.php the test file will be in src / Blogger / BlogBundle / Tests / Entity / BlogTest.php . An example will look like
in the following way
 src/Blogger/BlogBundle/ Entity/ Blog.php Comment.php Controller/ PageController.php Twig/ Extensions/ BloggerBlogExtension.php Tests/ Entity/ BlogTest.php CommentTest.php Controller/ PageControllerTest.php Twig/ Extensions/ BloggerBlogExtensionTest.php 



Please note that each of the test files has a Test suffix.

Testing Blog Entity - the slugify method


We will begin testing the slugify method in the essence of Blog. Let's write some tests to make sure this method works correctly. Create a new file located in
src / Blogger / BlogBundle / Tests / Entity / BlogTest.php
 <?php // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php namespace Blogger\BlogBundle\Tests\Entity; use Blogger\BlogBundle\Entity\Blog; class BlogTest extends \PHPUnit_Framework_TestCase { } 



We created a test class for the Blog entity. Please note that the location of the file corresponds to the folder structure mentioned above. The Blog Test class extends the base PHPUnit class PHPUnit_Framework_TestCase. All tests you write for PHPUnit will be descendants of this class. You remember from the previous parts that \ must be placed before the name of the class PHPUnit_Framework_TestCase since the class is declared in the common PHP namespace.

Now we have a skeleton class for the Blog Entity tests, let's write a test. Tests in PHPUnit are Test class methods with the test prefix, for example testSlugify () . Update your BlogTest located
src / Blogger / BlogBundle / Tests / Entity / BlogTest.php
 // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php // .. class BlogTest extends \PHPUnit_Framework_TestCase { public function testSlugify() { $blog = new Blog(); $this->assertEquals('hello-world', $blog->slugify('Hello World')); } } 



This is a very simple test. It creates a new Blog entity and runs assertEquals () for the result of the slugify method. The assertEquals () method takes 2 required arguments, the expected result and the actual result. An optional third argument may be passed to output a message when the test fails.

Let's run our new unit test. Type the following command in the console.

 $ phpunit -c app 


You should see the following.

 PHPUnit 5.4.6 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Time: 348 ms, Memory: 13.25MB OK (1 test, 1 assertion) 


The PHPPunit output is very simple, at first, some information about PHPUnit and the number of each running test is shown, in our case we only have 1 test. At the end he informs us about the test results. For our BlogTest, we launched only 1 test with 1 statement. If you have configured to display colors in your command line, you will also see that the last line is colored green, indicating that everything is OK . Let's update the testSlugify () method to see what happens when the test fails.
 // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php // .. public function testSlugify() { $blog = new Blog(); $this->assertEquals('hello-world', $blog->slugify('Hello World')); $this->assertEquals('a day with symfony2', $blog->slugify('A Day With Symfony2')); } 


Restart the unit test as before. Will be displayed
following
 PHPUnit 5.4.6 by Sebastian Bergmann and contributors. F 1 / 1 (100%) Time: 340 ms, Memory: 13.25MB There was 1 failure: 1) Blogger\BlogBundle\Tests\Entity\BlogTest::testSlugify Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'a day with symfony2' +'a-day-with-symfony2' D:\local\symfony-blog\src\Blogger\BlogBundle\Tests\Entity\BlogTest.php:15 FAILURES! Tests: 1, Assertions: 2, Failures: 1. 



The output is a bit more this time. The F character tells us that the test fails. You will also see the E symbol if your test contains errors. Then PHPUnit notifies us of the details of the failures, in this case one. We can see that the Blogger \ BlogBundle \ Tests \ Entity \ BlogTest :: testSlugify method failed because the expected and actual values ​​are different. If you have a color output in your command line, you will also see that the last line is colored red, indicating that there are errors in the test. Correct the testSlugify () method so that it is successfully executed.

 // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php // .. public function testSlugify() { $blog = new Blog(); $this->assertEquals('hello-world', $blog->slugify('Hello World')); $this->assertEquals('a-day-with-symfony2', $blog->slugify('A Day With Symfony2')); } 


Before going further add a few more tests.
for slugify () method
 // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php // .. public function testSlugify() { $blog = new Blog(); $this->assertEquals('hello-world', $blog->slugify('Hello World')); $this->assertEquals('a-day-with-symfony2', $blog->slugify('A Day With Symfony2')); $this->assertEquals('hello-world', $blog->slugify('Hello world')); $this->assertEquals('symblog', $blog->slugify('symblog ')); $this->assertEquals('symblog', $blog->slugify(' symblog')); } 



Now that we have tested the slugify method, we need to make sure that Blog $ slug is set correctly when Blog $ title is updated. Add the following methods to the file
src / Blogger / BlogBundle / Tests / Entity / BlogTest.php
 // src/Blogger/BlogBundle/Tests/Entity/BlogTest.php // .. public function testSetSlug() { $blog = new Blog(); $blog->setSlug('Symfony2 Blog'); $this->assertEquals('symfony2-blog', $blog->getSlug()); } public function testSetTitle() { $blog = new Blog(); $blog->setTitle('Hello World'); $this->assertEquals('hello-world', $blog->getSlug()); } 



We started by testing the setSlug method to ensure that the $ slug element correctly adds slug when updating. Next, we check that $ slug is updated correctly when the setTitle method is called by the Blog entity.
Run tests to check the performance of the Blog Entity.

Twig Extension Testing


In the previous section, we created a Twig extension to convert a \ DateTime into a string to determine the elapsed time since the comment was posted. Create a new file
src / Blogger / BlogBundle / Tests / Twig / Extensions / BloggerBlogExtensionTest.php
 <?php // src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php namespace Blogger\BlogBundle\Tests\Twig\Extensions; use Blogger\BlogBundle\Twig\Extensions\BloggerBlogExtension; class BloggerBlogExtensionTest extends \PHPUnit_Framework_TestCase { public function testCreatedAgo() { $blog = new BloggerBlogExtension(); $this->assertEquals("0 seconds ago", $blog->createdAgo(new \DateTime())); $this->assertEquals("34 seconds ago", $blog->createdAgo($this->getDateTime(-34))); $this->assertEquals("1 minute ago", $blog->createdAgo($this->getDateTime(-60))); $this->assertEquals("2 minutes ago", $blog->createdAgo($this->getDateTime(-120))); $this->assertEquals("1 hour ago", $blog->createdAgo($this->getDateTime(-3600))); $this->assertEquals("1 hour ago", $blog->createdAgo($this->getDateTime(-3601))); $this->assertEquals("2 hours ago", $blog->createdAgo($this->getDateTime(-7200))); // Cannot create time in the future $this->setExpectedException('\InvalidArgumentException'); $blog->createdAgo($this->getDateTime(60)); } protected function getDateTime($delta) { return new \DateTime(date("Ymd H:i:s", time()+$delta)); } } 



The class is configured the same way as before, creating the testCreatedAgo () method to test the Twig extension. We introduced another PHPUnit method in this test setExpectedException (). We know that the createdAgo method of Twig extension cannot handle dates in the future and will be passed to \ Exception. The getDateTime () method is auxiliary for creating an instance of \ DateTime. Please note that there is no test prefix, so PHPUnit will not try to run it as a test. Open a console and run tests for this file. We could just run the test as before, but we can also tell PHPUnit to run tests only for a specific folder (and subfolders) or a file. Enter the following command:

 $ phpunit -c app src/Blogger/BlogBundle/Tests/Twig/Extensions/BloggerBlogExtensionTest.php 


This will run tests for the BloggerBlogExtensionTest file only. PHPUnit will tell us that the test failed. The output is shown below.
 1) Blogger\BlogBundle\Tests\Twig\Extensions\BloggerBlogExtensionTest::testCreatedAgo Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'0 seconds ago' +'0 second ago' //.. 


We expected the first statement to return 0 seconds ago, but it did not return because the second word was not a plural number. Let's update the twig extension to fix this.
src / Blogger / BlogBundle / Twig / Extensions / BloggerBlogBundle.php
 <?php // src/Blogger/BlogBundle/Twig/Extensions/BloggerBlogBundle.php namespace Blogger\BlogBundle\Twig\Extensions; class BloggerBlogExtension extends \Twig_Extension { // .. public function createdAgo(\DateTime $dateTime) { // .. if ($delta < 60) { // Seconds $time = $delta; $duration = $time . " second" . (($time === 0 || $time > 1) ? "s" : "") . " ago"; } // .. } // .. } 



Restart Unit tests. You should see that the first statement is displayed correctly, but the test still fails. Let's consider the following conclusion.

 1) Blogger\BlogBundle\Tests\Twig\Extensions\BloggerBlogExtensionTest::testCreatedAgo Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'1 hour ago' +'60 minutes ago' 


We see that the fifth statement fails. Looking at the test, we can see that the Twig Expansion did not function correctly. Should have been returned 1 hour ago, but instead returned 60 minutes ago. If we look at the code in BloggerBlogExtension Twig we will see the reason. We compare the time that can be included, that is, we use <= instead of < . Update Twig extension to fix this.
src / Blogger / BlogBundle / Twig / Extensions / BloggerBlogBundle.php
 <?php // src/Blogger/BlogBundle/Twig/Extensions/BloggerBlogBundle.php namespace Blogger\BlogBundle\Twig\Extensions; class BloggerBlogExtension extends \Twig_Extension { // .. public function createdAgo(\DateTime $dateTime) { // .. else if ($delta < 3600) { // Mins $time = floor($delta / 60); $duration = $time . " minute" . (($time > 1) ? "s" : "") . " ago"; } else if ($delta < 86400) { // Hours $time = floor($delta / 3600); $duration = $time . " hour" . (($time > 1) ? "s" : "") . " ago"; } // .. } // .. } 



Now re-run all the tests with the following command.

 $ phpunit -c app 


This will run all our tests and show that all tests have been successfully passed. Although we have written only a small number of unit tests, you should get a sense of how powerful and important testing is when writing code. Testing also helps add any future functionality to the project without breaking the previous specifications. This concludes the unit testing review.

The board

Try adding your own unit tests.


Functional testing


We have written several unit tests, let's move on to testing several components together. The first section of functional testing will include simulating browser requests to test the generated responses.

Testing the About page

We'll start by testing the PageController class for the About page. Since the page is very simple, this is a good place to start. Create a new file
src / Blogger / BlogBundle / Tests / Controller / PageControllerTest.php
 <?php // src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php namespace Blogger\BlogBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class PageControllerTest extends WebTestCase { public function testAbout() { $client = static::createClient(); $crawler = $client->request('GET', '/about'); $this->assertEquals(1, $crawler->filter('h1:contains("About symblog")')->count()); } } 



We will see that the controller test is very similar to the class DefaultControllerTest. Testing the about page, checks the About symblog line that is present in the generated HTML, namely inside the H1 tag. The PageControllerTest class does not extend the \ PHPUnit_Framework_TestCase, as we saw in the unit testing example, instead it extends the WebTestCase class. This class is part of the symfony2 FrameworkBundle.

As explained above, the PHPUnit test classes should extend \ PHPUnit_Framework_TestCase, but when additional or common functionality is required in several tests, it is useful to encapsulate it in your own class and have your test classes extend it. WebTestCase does exactly that, it provides several useful methods for performing functional tests in Symfony2. Look at the WebTestCase file located at vendor / symfony / symfony / src / Symfony / Bundle / FrameworkBundle / Test / WebTestCase.php , you will see that this class is actually an extension of the KernelTestCase class.

 // vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/WebTestCase.php abstract class WebTestCase extends KernelTestCase { // .. } 


If you look at the createClient () method in the WebTestCase class, you can see that it creates an instance of the Symfony2 Kernel. Following the methods further, you will also notice that the environment is set to test (if not overridden as one of the createClient () arguments). This is the test environment we talked about in the previous section.

Looking at our test class, we can see that the createClient () method is called to get and run the test. Then we call the client-side request () method to simulate a browser HTTP GET request by the URL / about (this would be exactly the same if you visited the browser http: // localhost: 8000 / about ). The request gives us a Crawler object that contains the response. The Crawler class is very useful because it allows us to move the returned HTML. We use an instance of Crawler to verify that the H1 tag in the HTML response contains the words About symblog. You will notice that even if we extend the WebTestCase class, we still use the Assert method, as before (remember, the PageControllerTest class is still a descendant of the \ PHPUnit_Framework_TestCase class).

Let's run PageControllerTest, with the following command. When writing tests, it is useful to use it to perform tests only for the file in which you are currently working.

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php 


The message OK (1 test, 1 assertion) should be displayed letting us know that 1 test (testAbout () was running), with 1 statement (assertEquals ()).

Try changing the line About Symblog to Contact, and then rerun the test. The test will fail because Contact is not found, with the result that assertEquals will be set to false.
Return the About symblog line back before we continue.
A Crawler instance allows you to view HTML or XML documents (which means that the Crawler will only work with responses that return HTML or XML). We can use Crawler to bypass the generated response using methods such as filter (), first (), last () and parents (). If you used jQuery before, you should feel right at home with the Crawler class. A full list of supported Crawler traversal methods can be found in the Testing chapter of the Symfony2 book.

Homepage


While the test for the About page was simple, he outlined the basic principles of functional testing of the site page.

Customer creation

Page request

Response check

This is a simple overview of the process, in fact there are a number of other steps that we could take such as clicking on links, filling out and submitting a form.
Let's create a method to test the home page. We know that the main page is accessible along the route / and that it should display the latest blog entries. Add a new testIndex () method to the PageControllerTest class located
src / Blogger / BlogBundle / Tests / Controller / PageControllerTest.php
 // src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php public function testIndex() { $client = static::createClient(); $crawler = $client->request('GET', '/'); // Check there are some blog entries on the page $this->assertTrue($crawler->filter('article.blog')->count() > 0); } 



You can see the same steps as with the dough for the about page. Run the test to make sure everything works correctly.

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php 


Let's go now a little further. Part of the functional testing includes the ability to repeat what the user will do on the site. In order to navigate through the pages of your site, users click on links. Let's simulate this action now to check if the links to the post page are correct when clicking on the header.
Update the testIndex () method in the PageControllerTest class
// src / Blogger / BlogBundle / Tests / Controller / PageControllerTest.php

public function testIndex ()
{
// ...

// Find the first link
$ blogLink = $ crawler-> filter ('article.blog h2 a') -> first ();
$ blogTitle = $ blogLink-> text ();
$ crawler = $ client-> click ($ blogLink-> link ());

// Check the h2 has the blog title in it
$ this-> assertEquals (1, $ crawler-> filter ('h2: contains ("'. $ blogTitle. '")') -> count ());
}


The first thing we do is use a Crawler to extract the text from the first link of the title of the record. This is done using the article.blog h2 a filter. This filter is used to return the a tag in the H2 article.blog article. To better understand this, take a look at the markup used on the home page to display the entry.
 <article class="blog"> <div class="date"><time datetime="2016-06-17T14:23:55+03:00">Friday, June 17, 2016</time></div> <header> <h2><a href="/1/a-day-with-symfony2">A day with Symfony2</a></h2> </header> //.. </article> 


You can see the structure of the article.blog h2 a filter in the homepage markup. You will also notice that there are several in the markup, which means that the Crawler filter returns collections. Since we only want the first link, we use the first () method in the collection. The click () method takes a reference object and returns the response in the Crawler instance. You will notice that the Crawler object is a key element for functional testing.
The Crawler object now contains the response of the blog page. We need to test that the link redirects us to the right page. We can use the $ blogTitle value we extracted earlier to check the title in the response.
Run the tests to ensure that the navigation between the home page and the recording page is working properly.

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php 


Now that you have an understanding of how to navigate through the pages of a site with functional testing, let's move on to testing forms.

Testing your contact page


symblog , http://localhost:8000/contact . , . , , ( , ).











1 5. , 3 .

testContact() PageControllerTest,
src / Blogger / BlogBundle / Tests / Controller / PageControllerTest.php
 // src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php public function testContact() { $client = static::createClient(); $crawler = $client->request('GET', '/contact'); $this->assertEquals(1, $crawler->filter('h1:contains("Contact symblog")')->count()); // Select based on button value, or id or name for buttons $form = $crawler->selectButton('Submit')->form(); $form['contact[name]'] = 'name'; $form['contact[email]'] = 'email@email.com'; $form['contact[subject]'] = 'Subject'; $form['contact[body]'] = 'The comment body must be at least 50 characters long as there is a validation constrain on the Enquiry entity'; $crawler = $client->submit($form); $this->assertEquals(1, $crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count()); } 



We will start with the url / contact request and verify that the page contains the correct H1 header. Next we use Crawler to select the form submit button. The reason that we choose the button, and not the form, is that the form may contain several buttons that we may want to press independently of each other. From the selected button we can get the form. We can set the form values ​​using the [] array. Finally, the form is submitted to the client’s submit () method to submit the form. As usual, we get a copy of the Crawler back. Using the Crawler response, we verify that the flash message is present in the returned response. Perform a test to verify that everything is functioning normally.

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php 


The test failed. We will get the following PHPUnit message

 1) Blogger\BlogBundle\Tests\Controller\PageControllerTest::testContact Failed asserting that 0 matches expected 1. D:\local\symfony-blog\src\Blogger\BlogBundle\Tests\Controller\PageControllerTest.php:55 FAILURES! Tests: 3, Assertions: 5, Failures: 1. 


The output informs us that the flash message cannot be found in the response after the form has been submitted. This happened because when we are in a test environment, no redirections are performed. When a form is successfully tested in the PageController class, a redirect occurs. This redirection does not occur; We need to explicitly state that it should be respected. The reason that redirects are not performed is simple, because you may want to check the current response first. We will see this later when we check to see if the message went. Update the PageControllerTest class to set the redirection.

 // src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php public function testContact() { // .. $crawler = $client->submit($form); // Need to follow redirect $crawler = $client->followRedirect(); $this->assertEquals(1, $crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count()); } 


Now if we run the PHPUnit tests, we must pass them. Let's now look at the final stage of checking the process of sending a contact form, step 4, checking that the mail has been sent. We already know that mail will not be delivered in the test environment due to the following configuration.

 # app/config/config_test.yml swiftmailer: disable_delivery: true 


We can verify the fact that the letters were sent using the information collected using the web profiler. This is where it is important not to redirect the client. The profiler check must be done before the redirection occurs, otherwise the profiler information will be lost.
Update testContact () method
 // src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php public function testContact() { // .. $crawler = $client->submit($form); // Check email has been sent if ($profile = $client->getProfile()) { $swiftMailerProfiler = $profile->getCollector('swiftmailer'); // Only 1 message should have been sent $this->assertEquals(1, $swiftMailerProfiler->getMessageCount()); // Get the first message $messages = $swiftMailerProfiler->getMessages(); $message = array_shift($messages); $symblogEmail = $client->getContainer()->getParameter('blogger_blog.emails.contact_email'); // Check message is being sent to correct address $this->assertArrayHasKey($symblogEmail, $message->getTo()); } // Need to follow redirect $crawler = $client->followRedirect(); $this->assertTrue($crawler->filter('.blogger-notice:contains("Your contact enquiry was successfully sent. Thank you!")')->count() > 0); } 



After submitting the form, we check if the profiler is available, since this can be turned off using the configuration for the current environment.



, , , , .


swiftmailer . swiftmailer , , . , , .
getMessageCount(), , 1 . , , , , .
, .

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/PageControllerTest.php 



, , . , , .









, ( )


,
src/Blogger/BlogBundle/Tests/Controller/BlogControllerTest.php
 <?php // src/Blogger/BlogBundle/Tests/Controller/BlogControllerTest.php namespace Blogger\BlogBundle\Tests\Controller; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class BlogControllerTest extends WebTestCase { public function testAddBlogComment() { $client = static::createClient(); $crawler = $client->request('GET', '/1/a-day-with-symfony'); $this->assertEquals(1, $crawler->filter('h2:contains("A day with Symfony2")')->count()); // Select based on button value, or id or name for buttons $form = $crawler->selectButton('Submit')->form(); $crawler = $client->submit($form, array( 'blogger_blogbundle_commenttype[user]' => 'name', 'blogger_blogbundle_commenttype[comment]' => 'comment', )); // Need to follow redirect $crawler = $client->followRedirect(); // Check comment is now displaying on page, as the last entry. This ensure comments // are posted in order of oldest to newest $articleCrawler = $crawler->filter('section .previous-comments article')->last(); $this->assertEquals('name', $articleCrawler->filter('header span.highlight')->text()); $this->assertEquals('comment', $articleCrawler->filter('p')->last()->text()); // Check the sidebar to ensure latest comments are display and there is 10 of them $this->assertEquals(10, $crawler->filter('aside.sidebar section')->last() ->filter('article')->count() ); $this->assertEquals('name', $crawler->filter('aside.sidebar section')->last() ->filter('article')->first() ->filter('header span.highlight')->text() ); } } 



, , , .
 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Controller/BlogControllerTest.php 


PHPUnit should tell you that 1 test was successfully completed. Looking at the code for testAddBlogComment () we can see the creation of the client, the page request and its verification. Then proceed to the receipt of the form Adding a comment and submitting the form. The way in which we fill out the form values ​​is slightly different from the previous version. This time we use the 2nd argument of the submit () client method to add form values.

Tip

We could also use an object-oriented interface to set form field values. Examples are shown below.

 // Tick a checkbox $form['show_emal']->tick(); // Select an option or a radio $form['gender']->select('Male'); 



After submitting the form, we request a client who must follow the redirection so that we can verify the response. We use Crawler again to get the latest blog comment, which should be the one we just posted. Finally we check the latest comments in the sidebar to check that the comment is first on the list.

Blog repository

In the last part of functional testing, we explore the testing of the Doctrine 2 Repository. Create a new file
src / Blogger / BlogBundle / Tests / Repository / BlogRepositoryTest.php
 <?php // src/Blogger/BlogBundle/Tests/Repository/BlogRepositoryTest.php namespace Blogger\BlogBundle\Tests\Repository; use Blogger\BlogBundle\Entity\Repository\BlogRepository; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class BlogRepositoryTest extends WebTestCase { /** * @var \Blogger\BlogBundle\Entity\Repository\BlogRepository */ private $blogRepository; public function setUp() { $kernel = static::createKernel(); $kernel->boot(); $this->blogRepository = $kernel->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('BloggerBlogBundle:Blog'); } public function testGetTags() { $tags = $this->blogRepository->getTags(); $this->assertTrue(count($tags) > 1); $this->assertContains('symblog', $tags); } public function testGetTagWeights() { $tagsWeight = $this->blogRepository->getTagWeights( array('php', 'code', 'code', 'symblog', 'blog') ); $this->assertTrue(count($tagsWeight) > 1); // Test case where count is over max weight of 5 $tagsWeight = $this->blogRepository->getTagWeights( array_fill(0, 10, 'php') ); $this->assertTrue(count($tagsWeight) >= 1); // Test case with multiple counts over max weight of 5 $tagsWeight = $this->blogRepository->getTagWeights( array_merge(array_fill(0, 10, 'php'), array_fill(0, 2, 'html'), array_fill(0, 6, 'js')) ); $this->assertEquals(5, $tagsWeight['php']); $this->assertEquals(3, $tagsWeight['js']); $this->assertEquals(1, $tagsWeight['html']); // Test empty case $tagsWeight = $this->blogRepository->getTagWeights(array()); $this->assertEmpty($tagsWeight); } } 


Since we want to run tests that require a proper connection to the database, we will again extend WebTestCase, as this allows you to load the Symfony2 Kernel. Run a test for this file using the following command.

 $ phpunit -c app/ src/Blogger/BlogBundle/Tests/Repository/BlogRepositoryTest.php 


Code coverage

Code coverage gives us an understanding of which parts of the code are executed when the tests run. With this, we can see parts of our code that are not covered by tests and determine whether to write a test for them.

To display the code coverage for your application, enter in the console

 $ phpunit --coverage-html ./phpunit-report -c app/ 


Note that the command worked, you need activated xdebug in php.
This will display the code coverage analysis in the phpunit-report folder in the project root. Open the index.html file from this folder in your browser to see the result of the analysis.



Conclusion



. , , . , Symfony2 Crawler .

:

https://symfony.com/
http://tutorial.symblog.co.uk/
https://phpunit.de/

Post Scriptum
, , , .


1 — Symfony2
2 — : ,
3 — Doctrine 2
4 — , Doctrine 2
5 — Twig , (sidebar) Assetic


Also, if you like the guide, you can put a star to the project repository or subscribe. Thank.

Source: https://habr.com/ru/post/303578/


All Articles