Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 90188e7

Browse files
committedApr 17, 2021
[Testing] Reorganized "Application Tests" section
1 parent 161fb45 commit 90188e7

File tree

3 files changed

+171
-131
lines changed

3 files changed

+171
-131
lines changed
 

‎.doctor-rst.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,4 @@ whitelist:
9595
- ".. _`Deploying Symfony 4 Apps on Heroku`: https://devcenter.heroku.com/articles/deploying-symfony4"
9696
- '.. versionadded:: 0.2' # MercureBundle
9797
- '.. code-block:: twig'
98+
- 'End to End Tests (E2E)'

‎testing.rst

+162-126
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ Symfony integrates with an independent library called `PHPUnit`_ to give
1515
you a rich testing framework. This article won't cover PHPUnit itself,
1616
which has its own excellent `documentation`_.
1717

18-
Before creating your first test, install PHPUnit with the following
19-
command:
18+
Before creating your first test, install ``phpunit/phpunit`` and the
19+
``symfony/test-pack``, which installs some other packages providing useful
20+
Symfony test utilities:
2021

2122
.. code-block:: terminal
2223
23-
$ composer require --dev phpunit/phpunit
24+
$ composer require --dev phpunit/phpunit symfony/test-pack
2425
2526
After the library is installed, try running PHPUnit:
2627

@@ -69,9 +70,9 @@ Unit Tests
6970

7071
A `unit test`_ ensures that individual units of source code (e.g. a single
7172
class or some specific method in some class) meet their design and behave
72-
as intended. Writing unit tests in a Symfony application no different from
73-
writing standard PHPUnit unit tests. You can learn about it in the PHPUnit
74-
documentation: `Writing Tests for PHPUnit`_.
73+
as intended. Writing unit tests in a Symfony application is no different
74+
from writing standard PHPUnit unit tests. You can learn about it in the
75+
PHPUnit documentation: `Writing Tests for PHPUnit`_.
7576

7677
By convention, the ``tests/`` directory should replicate the directory
7778
of your application for unit tests. So, if you're testing a class in the
@@ -95,7 +96,9 @@ You can run tests using the ``./vendor/bin/phpunit`` command:
9596
.. tip::
9697

9798
In large test suites, it can make sense to create subdirectories for
98-
each type of tests (e.g. ``tests/unit/`` and ``test/functional/``).
99+
each type of tests (e.g. ``tests/Unit/`` and ``test/Functional/``).
100+
101+
.. _integration-tests:
99102

100103
Integration Tests
101104
-----------------
@@ -118,7 +121,6 @@ class to help you creating and booting the kernel in your tests using
118121
{
119122
public function testSomething()
120123
{
121-
// boot the Symfony kernel
122124
self::bootKernel();
123125

124126
// ...
@@ -198,7 +200,15 @@ method::
198200
.. tip::
199201

200202
It is recommended to run your test with ``debug`` set to ``false`` on
201-
your CI server, as it significantly improves test performance.
203+
your CI server, as it significantly improves test performance. This
204+
disables clearing the cache. If your tests don't run in a clean
205+
environment each time, you have to manually clear it using for instance
206+
this code in ``tests/bootstrap.php``::
207+
208+
// ...
209+
210+
// ensure a fresh cache when debug mode is disabled
211+
(new \Symfony\Component\Filesystem\Filesystem())->remove(__DIR__.'/../var/cache/test');
202212

203213
Customizing Environment Variables
204214
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -243,7 +253,7 @@ the container is stored in ``self::$container``::
243253
{
244254
public function testSomething()
245255
{
246-
// boot the Symfony kernel
256+
// (1) boot the Symfony kernel
247257
self::bootKernel();
248258

249259
// (2) use self::$container to access the service container
@@ -267,6 +277,8 @@ It gives you access to both the public services and the non-removed
267277
are not used by any other services), you need to declare those private
268278
services as public in the ``config/services_test.yaml`` file.
269279

280+
.. _testing-databases:
281+
270282
Configuring a Database for Tests
271283
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
272284

@@ -410,21 +422,35 @@ have a very specific workflow:
410422
#. Test the response;
411423
#. Rinse and repeat.
412424

413-
Before creating your first test, install the ``symfony/test-pack`` which
414-
requires multiple packages providing some of the utilities used in the
415-
tests:
416-
417-
.. code-block:: terminal
425+
.. note::
418426

419-
$ composer require --dev symfony/test-pack
427+
The tools used in this section can be installed via the ``symfony/test-pack``,
428+
use ``composer require symfony/test-pack`` if you haven't done so already.
420429

421430
Write Your First Application Test
422431
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
423432

424-
Application tests are PHP files that typically live in the ``tests/Controller``
425-
directory of your application. If you want to test the pages handled by your
426-
``PostController`` class, start by creating a new ``PostControllerTest.php``
427-
file that extends a special ``WebTestCase`` class::
433+
Application tests are PHP files that typically live in the ``tests/Controller/``
434+
directory of your application. They often extend
435+
:class:`Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase`. This class
436+
adds special logic on top of the ``KernelTestCase``. You can read more
437+
about that in the above :ref:`section on integration tests <integration-tests>`.
438+
439+
If you want to test the pages handled by your
440+
``PostController`` class, start by creating a new ``PostControllerTest``
441+
using the ``make:test`` command of the `SymfonyMakerBundle`_:
442+
443+
.. code-block:: terminal
444+
445+
$ php bin/console make:test
446+
447+
Which test type would you like?:
448+
> WebTestCase
449+
450+
The name of the test class (e.g. BlogPostTest):
451+
> Controller\PostControllerTest
452+
453+
This creates the following test class::
428454

429455
// tests/Controller/PostControllerTest.php
430456
namespace App\Tests\Controller;
@@ -433,87 +459,36 @@ file that extends a special ``WebTestCase`` class::
433459

434460
class PostControllerTest extends WebTestCase
435461
{
436-
public function testShowPost()
462+
public function testSomething(): void
437463
{
464+
// This calls KernelTestCase::bootKernel(), and creates a
465+
// "client" that is acting as the browser
438466
$client = static::createClient();
439467

440-
$client->request('GET', '/post/hello-world');
468+
// Request a specific page
469+
$crawler = $client->request('GET', '/');
441470

442-
$this->assertEquals(200, $client->getResponse()->getStatusCode());
471+
// Validate a successful response and some content
472+
$this->assertResponseIsSuccessful();
473+
$this->assertSelectorTextContains('h1', 'Hello World');
443474
}
444475
}
445476

446-
In the above example, you validated that the HTTP response was successful. The
447-
next step is to validate that the page actually contains the expected content.
448-
The ``createClient()`` method returns a client, which is like a browser that
449-
you'll use to crawl your site::
477+
In the above example, the test validates that the HTTP response was successful
478+
and the request body contains a ``<h1>`` tag with ``"Hello world"``. The
479+
``createClient()`` method also returns a client, which is like a browser
480+
that you can use to crawl your site::
450481

451482
$crawler = $client->request('GET', '/post/hello-world');
452483

484+
// for instance, count the number of ``.comment`` elements on the page
485+
$this->assertCount(4, $crawler->filter('.comment'));
486+
453487
The ``request()`` method (read
454488
:ref:`more about the request method <testing-request-method-sidebar>`)
455489
returns a :class:`Symfony\\Component\\DomCrawler\\Crawler` object which can
456490
be used to select elements in the response, click on links and submit forms.
457491

458-
Useful Assertions
459-
~~~~~~~~~~~~~~~~~
460-
461-
To get you started faster, here is a list of the most common and
462-
useful test assertions::
463-
464-
use Symfony\Component\HttpFoundation\Response;
465-
466-
// ...
467-
468-
// asserts that there is at least one h2 tag with the class "subtitle"
469-
// the third argument is an optional message shown on failed tests
470-
$this->assertGreaterThan(0, $crawler->filter('h2.subtitle')->count(),
471-
'There is at least one subtitle'
472-
);
473-
474-
// asserts that there are exactly 4 h2 tags on the page
475-
$this->assertCount(4, $crawler->filter('h2'));
476-
477-
// asserts that the "Content-Type" header is "application/json"
478-
$this->assertResponseHeaderSame('Content-Type', 'application/json');
479-
// equivalent to:
480-
$this->assertTrue($client->getResponse()->headers->contains(
481-
'Content-Type', 'application/json'
482-
));
483-
484-
// asserts that the response content contains a string
485-
$this->assertContains('foo', $client->getResponse()->getContent());
486-
// ...or matches a regex
487-
$this->assertRegExp('/foo(bar)?/', $client->getResponse()->getContent());
488-
489-
// asserts that the response status code is 2xx
490-
$this->assertResponseIsSuccessful();
491-
// equivalent to:
492-
$this->assertTrue($client->getResponse()->isSuccessful());
493-
494-
// asserts that the response status code is 404 Not Found
495-
$this->assertTrue($client->getResponse()->isNotFound());
496-
497-
// asserts a specific status code
498-
$this->assertResponseStatusCodeSame(201);
499-
// HTTP status numbers are available as constants too:
500-
// e.g. 201 === Symfony\Component\HttpFoundation\Response::HTTP_CREATED
501-
// equivalent to:
502-
$this->assertEquals(201, $client->getResponse()->getStatusCode());
503-
504-
// asserts that the response is a redirect to /demo/contact
505-
$this->assertResponseRedirects('/demo/contact');
506-
// equivalent to:
507-
$this->assertTrue($client->getResponse()->isRedirect('/demo/contact'));
508-
// ...or check that the response is a redirect to any URL
509-
$this->assertResponseRedirects();
510-
511-
.. versionadded:: 4.3
512-
513-
The ``assertResponseHeaderSame()``, ``assertResponseIsSuccessful()``,
514-
``assertResponseStatusCodeSame()``, ``assertResponseRedirects()`` and other
515-
related methods were introduced in Symfony 4.3.
516-
517492
Working with the Test Client
518493
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
519494

@@ -527,41 +502,9 @@ returns a ``Crawler`` instance.
527502

528503
.. tip::
529504

530-
Hardcoding the request URLs is a best practice for application tests. If the
531-
test generates URLs using the Symfony router, it won't detect any change
532-
made to the application URLs which may impact the end users.
533-
534-
.. _testing-request-method-sidebar:
535-
536-
.. sidebar:: More about the ``request()`` Method:
537-
538-
The full signature of the ``request()`` method is::
539-
540-
request(
541-
$method,
542-
$uri,
543-
array $parameters = [],
544-
array $files = [],
545-
array $server = [],
546-
$content = null,
547-
$changeHistory = true
548-
)
549-
550-
The ``server`` array is the raw values that you'd expect to normally
551-
find in the PHP `$_SERVER`_ superglobal. For example, to set the
552-
``Content-Type`` and ``Referer`` HTTP headers, you'd pass the following (mind
553-
the ``HTTP_`` prefix for non standard headers)::
554-
555-
$client->request(
556-
'GET',
557-
'/post/hello-world',
558-
[],
559-
[],
560-
[
561-
'CONTENT_TYPE' => 'application/json',
562-
'HTTP_REFERER' => '/foo/bar',
563-
]
564-
);
505+
Hardcoding the request URLs is a best practice for application tests.
506+
If the test generates URLs using the Symfony router, it won't detect
507+
any change made to the application URLs which may impact the end users.
565508

566509
Use the crawler to find DOM elements in the response. These elements can then
567510
be used to click on links and submit forms::
@@ -622,6 +565,38 @@ script::
622565

623566
$client->insulate();
624567

568+
.. _testing-request-method-sidebar:
569+
570+
.. sidebar:: More about the ``request()`` Method:
571+
572+
The full signature of the ``request()`` method is::
573+
574+
request(
575+
$method,
576+
$uri,
577+
array $parameters = [],
578+
array $files = [],
579+
array $server = [],
580+
$content = null,
581+
$changeHistory = true
582+
)
583+
584+
The ``server`` array is the raw values that you'd expect to normally
585+
find in the PHP `$_SERVER`_ superglobal. For example, to set the
586+
``Content-Type`` and ``Referer`` HTTP headers, you'd pass the following (mind
587+
the ``HTTP_`` prefix for non standard headers)::
588+
589+
$client->request(
590+
'GET',
591+
'/post/hello-world',
592+
[],
593+
[],
594+
[
595+
'CONTENT_TYPE' => 'application/json',
596+
'HTTP_REFERER' => '/foo/bar',
597+
]
598+
);
599+
625600
AJAX Requests
626601
.............
627602

@@ -757,6 +732,65 @@ to be reported by PHPUnit::
757732

758733
$client->catchExceptions(false);
759734

735+
Useful Assertions
736+
~~~~~~~~~~~~~~~~~
737+
738+
To get you started faster, here is a list of the most common and
739+
useful test assertions::
740+
741+
use Symfony\Component\HttpFoundation\Response;
742+
743+
// ...
744+
745+
// asserts that there is at least one h2 tag with the class "subtitle"
746+
// the third argument is an optional message shown on failed tests
747+
$this->assertGreaterThan(0, $crawler->filter('h2.subtitle')->count(),
748+
'There is at least one subtitle'
749+
);
750+
751+
// asserts that there are exactly 4 h2 tags on the page
752+
$this->assertCount(4, $crawler->filter('h2'));
753+
754+
// asserts that the "Content-Type" header is "application/json"
755+
$this->assertResponseHeaderSame('Content-Type', 'application/json');
756+
// equivalent to:
757+
$this->assertTrue($client->getResponse()->headers->contains(
758+
'Content-Type', 'application/json'
759+
));
760+
761+
// asserts that the response content contains a string
762+
$this->assertContains('foo', $client->getResponse()->getContent());
763+
// ...or matches a regex
764+
$this->assertRegExp('/foo(bar)?/', $client->getResponse()->getContent());
765+
766+
// asserts that the response status code is 2xx
767+
$this->assertResponseIsSuccessful();
768+
// equivalent to:
769+
$this->assertTrue($client->getResponse()->isSuccessful());
770+
771+
// asserts that the response status code is 404 Not Found
772+
$this->assertTrue($client->getResponse()->isNotFound());
773+
774+
// asserts a specific status code
775+
$this->assertResponseStatusCodeSame(201);
776+
// HTTP status numbers are available as constants too:
777+
// e.g. 201 === Symfony\Component\HttpFoundation\Response::HTTP_CREATED
778+
// equivalent to:
779+
$this->assertEquals(201, $client->getResponse()->getStatusCode());
780+
781+
// asserts that the response is a redirect to /demo/contact
782+
$this->assertResponseRedirects('/demo/contact');
783+
// equivalent to:
784+
$this->assertTrue($client->getResponse()->isRedirect('/demo/contact'));
785+
// ...or check that the response is a redirect to any URL
786+
$this->assertResponseRedirects();
787+
788+
.. versionadded:: 4.3
789+
790+
The ``assertResponseHeaderSame()``, ``assertResponseIsSuccessful()``,
791+
``assertResponseStatusCodeSame()``, ``assertResponseRedirects()`` and other
792+
related methods were introduced in Symfony 4.3.
793+
760794
.. index::
761795
single: Tests; Crawler
762796

@@ -988,10 +1022,10 @@ their type::
9881022
$client->submit($form, [], ['HTTP_ACCEPT_LANGUAGE' => 'es']);
9891023
$client->submitForm($button, [], 'POST', ['HTTP_ACCEPT_LANGUAGE' => 'es']);
9901024

991-
End to End Tests (E2E)
992-
----------------------
993-
9941025
.. TODO
1026+
End to End Tests (E2E)
1027+
----------------------
1028+
9951029
* panther
9961030
* testing javascript
9971031
* UX or form collections as example?
@@ -1042,8 +1076,8 @@ configuration adds tests from a custom ``lib/tests`` directory:
10421076
<!-- ... -->
10431077
</phpunit>
10441078
1045-
To include other directories in the code coverage, also edit the ``<filter>``
1046-
section:
1079+
To include other directories in the `code coverage analysis`_, also edit the
1080+
``<filter>`` section:
10471081

10481082
.. code-block:: xml
10491083
@@ -1078,6 +1112,8 @@ Learn more
10781112
.. _`documentation`: https://phpunit.readthedocs.io/
10791113
.. _`Writing Tests for PHPUnit`: https://phpunit.readthedocs.io/en/stable/writing-tests-for-phpunit.html
10801114
.. _`unit test`: https://en.wikipedia.org/wiki/Unit_testing
1115+
.. _`DAMADoctrineTestBundle`: https://github.com/dmaicher/doctrine-test-bundle
1116+
.. _`DoctrineFixturesBundle documentation`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
10811117
.. _`$_SERVER`: https://www.php.net/manual/en/reserved.variables.server.php
10821118
.. _`SymfonyMakerBundle`: https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
10831119
.. _`code coverage analysis`: https://phpunit.readthedocs.io/en/9.1/code-coverage-analysis.html

‎testing/database.rst

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
.. index::
22
single: Tests; Database
33

4-
How to Test Code that Interacts with the Database
5-
=================================================
4+
How to Test A Doctrine Repository
5+
=================================
6+
7+
.. seealso::
8+
9+
The :ref:`main Testing guide <testing-databases>` describes how to use
10+
and set-up a database for your automated tests. The contents of this
11+
article show ways to test your Doctrine repositories.
612

713
Mocking a Doctrine Repository in Unit Tests
814
-------------------------------------------
@@ -134,6 +140,3 @@ so, get the entity manager via the service container as follows::
134140
$this->entityManager = null;
135141
}
136142
}
137-
138-
.. _`DAMADoctrineTestBundle`: https://github.com/dmaicher/doctrine-test-bundle
139-
.. _`DoctrineFixturesBundle documentation`: https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html

0 commit comments

Comments
 (0)
Please sign in to comment.