From f7d59f7d600364dbe8d61e207918ac17828f989e Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 2 Mar 2021 17:32:40 +0100 Subject: [PATCH 1/5] Dont mention PHPUnit bridge --- testing.rst | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/testing.rst b/testing.rst index dd122578599..a8bcd0b488e 100644 --- a/testing.rst +++ b/testing.rst @@ -15,35 +15,29 @@ Symfony integrates with an independent library called `PHPUnit`_ to give you a rich testing framework. This article won't cover PHPUnit itself, which has its own excellent `documentation`_. -Before creating your first test, install the `PHPUnit Bridge component`_, which -wraps the original PHPUnit binary to provide additional features: +Before creating your first test, install the PHPUnit with the following command: .. code-block:: terminal - $ composer require --dev symfony/phpunit-bridge + $ composer require --dev phpunit/phpunit -After the library downloads, try executing PHPUnit by running (the first time -you run this, it will download PHPUnit itself and make its classes available in -your app): +After the library is installed, try executing PHPUnit by running: .. code-block:: terminal - $ ./bin/phpunit + $ ./vendor/bin/phpunit .. note:: - The ``./bin/phpunit`` command is created by :ref:`Symfony Flex ` - when installing the ``phpunit-bridge`` package. If the command is missing, you - can remove the package (``composer remove symfony/phpunit-bridge``) and install - it again. Another solution is to remove the project's ``symfony.lock`` file and - run ``composer install`` to force the execution of all Symfony Flex recipes. + :ref:`Symfony Flex ` has automatically created ``phpunit.xml.dist`` + and ``tests/bootstrap.php``. If the files are missing, you can remove the package + (``composer remove phpunit/phpunit``) and install it again. Each test is a PHP class that should live in the ``tests/`` directory of your application. If you follow this rule, then you can run all of your application's tests with the same command as before. -PHPUnit is configured by the ``phpunit.xml.dist`` file in the root of your -Symfony application. +PHPUnit is configured by the ``phpunit.xml.dist`` file in the root of your application. .. tip:: @@ -1030,7 +1024,6 @@ Learn more .. _`PHPUnit`: https://phpunit.de/ .. _`documentation`: https://phpunit.readthedocs.io/ -.. _`PHPUnit Bridge component`: https://symfony.com/components/PHPUnit%20Bridge .. _`Writing Tests for PHPUnit`: https://phpunit.readthedocs.io/en/stable/writing-tests-for-phpunit.html .. _`unit test`: https://en.wikipedia.org/wiki/Unit_testing .. _`$_SERVER`: https://www.php.net/manual/en/reserved.variables.server.php From b0d51cf238d899db6eb63c81a4938bcc644352ab Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 2 Mar 2021 17:43:59 +0100 Subject: [PATCH 2/5] Update the definition of th test names. --- testing.rst | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/testing.rst b/testing.rst index a8bcd0b488e..0a96c473b54 100644 --- a/testing.rst +++ b/testing.rst @@ -39,14 +39,14 @@ application's tests with the same command as before. PHPUnit is configured by the ``phpunit.xml.dist`` file in the root of your application. -.. tip:: - - Use the ``--coverage-*`` command options to generate code coverage reports. - Read the PHPUnit manual to learn more about `code coverage analysis`_. - Types of Tests -------------- +To get a common language and shared context, it is important to define a what different +types of tests really mean. Symfony will use the following definition. If you have +learned something different, that is not necessarily wrong. It is just different +from what the Symfony documentation is using. + `Unit Tests`_ These tests ensure that *individual* units of source code (e.g. a single class) behave as intended. @@ -54,16 +54,12 @@ Types of Tests `Integration Tests`_ These tests test a combination of classes and commonly interact with Symfony's service container. These tests do not yet cover the full - working application, those are called *Functional tests*. + working application, those are called *Application tests*. -`Functional Tests`_ - Functional tests test the behavior of a complete application. They +`Application Tests`_ + Application tests test the behavior of a complete application. They make HTTP requests and test that the response is as expected. -`End to End Tests (E2E)`_ - At last, end to end tests test the application as a real user. They use - a real browser and real integrations with external services. - Unit Tests ---------- @@ -152,10 +148,10 @@ TODO .. _functional-tests: -Functional Tests +Application Tests ---------------- -Functional tests check the integration of the different layers of an +Application tests check the integration of the different layers of an application (from the routing to the views). They are no different from unit tests as far as PHPUnit is concerned, but they have a very specific workflow: @@ -175,7 +171,7 @@ tests: Set-up your Test Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Client used by functional tests creates a Kernel that runs in a special +The Client used by application tests creates a Kernel that runs in a special ``test`` environment. Since Symfony loads the ``config/packages/test/*.yaml`` in the ``test`` environment, you can tweak any of your application's settings specifically for testing. @@ -363,7 +359,7 @@ For more information, read the `DoctrineFixturesBundle documentation`_. Write Your First Functional Test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Functional tests are PHP files that typically live in the ``tests/Controller`` +Application tests are PHP files that typically live in the ``tests/Controller`` directory of your application. If you want to test the pages handled by your ``PostController`` class, start by creating a new ``PostControllerTest.php`` file that extends a special ``WebTestCase`` class. @@ -389,7 +385,7 @@ As an example, a test could look like this:: .. tip:: - To run your functional tests, the ``WebTestCase`` class needs to know which + To run your application tests, the ``WebTestCase`` class needs to know which is the application kernel to bootstrap it. The kernel class is usually defined in the ``KERNEL_CLASS`` environment variable (included in the default ``.env.test`` file provided by Symfony): @@ -482,7 +478,7 @@ returns a ``Crawler`` instance. .. tip:: - Hardcoding the request URLs is a best practice for functional tests. If the + Hardcoding the request URLs is a best practice for application tests. If the test generates URLs using the Symfony router, it won't detect any change made to the application URLs which may impact the end users. @@ -698,7 +694,7 @@ You can also override HTTP headers on a per request basis:: Reporting Exceptions .................... -Debugging exceptions in functional tests may be difficult because by default +Debugging exceptions in application tests may be difficult because by default they are caught and you need to look at the logs to see which exception was thrown. Disabling catching of exceptions in the test client allows the exception to be reported by PHPUnit:: From bd28a6403ac65ba629afb3b2be2c1a2ca797ad1d Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 2 Mar 2021 17:46:06 +0100 Subject: [PATCH 3/5] minors --- testing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testing.rst b/testing.rst index 0a96c473b54..7cceee8d21a 100644 --- a/testing.rst +++ b/testing.rst @@ -65,7 +65,7 @@ Unit Tests A `unit test`_ ensures that individual units of source code (e.g. a single class or some specific method in some class) meet their design and behave -as intended. Writing Symfony unit tests is no different from writing +as intended. Writing unit tests is a Symfony application no different from writing standard PHPUnit unit tests. You can learn about it in the PHPUnit documentation: `Writing Tests for PHPUnit`_. @@ -75,18 +75,18 @@ of your application for unit tests. So, if you're testing a class in the Autoloading is automatically enabled via the ``vendor/autoload.php`` file (as configured by default in the ``phpunit.xml.dist`` file). -You can run tests using the ``bin/phpunit`` command: +You can run tests using the ``./vendor/bin/phpunit`` command: .. code-block:: terminal # run all tests of the application - $ php bin/phpunit + $ php ./vendor/bin/phpunit # run all tests in the Util/ directory - $ php bin/phpunit tests/Util + $ php ./vendor/bin/phpunit tests/Util # run tests for the Calculator class - $ php bin/phpunit tests/Util/CalculatorTest.php + $ php ./vendor/bin/phpunit tests/Util/CalculatorTest.php Integration Tests ----------------- From 198b35a45f810a96eee8c82f1b9887f22d71675c Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 2 Mar 2021 18:08:25 +0100 Subject: [PATCH 4/5] Added info about integration tests --- testing.rst | 46 ++++++++-------------------------------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/testing.rst b/testing.rst index 7cceee8d21a..63a56a8dbfa 100644 --- a/testing.rst +++ b/testing.rst @@ -91,24 +91,11 @@ You can run tests using the ``./vendor/bin/phpunit`` command: Integration Tests ----------------- -TODO: KernelTestCase +An integration test will test a larger part of your application compared to a unit +test. Integration tests will use the Kernel to fetch a service from the dependency +injection container. -Accessing the Container -~~~~~~~~~~~~~~~~~~~~~~~ - -You can get the same container used in the application, which only includes -the public services:: - - public function testSomething() - { - $kernel = self::bootKernel(); - $container = $kernel->getContainer(); - $someService = $container->get('the-service-ID'); - - // ... - } - -Symfony tests also have access to a special container that includes both the +Symfony tests have access to a special container that includes both the public services and the non-removed :ref:`private services ` services:: @@ -118,29 +105,12 @@ services:: self::bootKernel(); $container = self::$container; - // $someService = $container->get('the-service-ID'); + $someService = $container->get('the-service-ID'); - // ... + $result = $someService->something(); + $this->assertTrue($result); } -.. TODO is this really different from self::$container and how to access - this in KernelTestCase? - - Finally, for the most rare edge-cases, Symfony includes a special container - which provides access to all services, public and private. This special - container is a service that can be get via the normal container:: - - public function testSomething() - { - $client = self::createClient(); - $normalContainer = $client->getContainer(); - $specialContainer = $normalContainer->get('test.service_container'); - - // $somePrivateService = $specialContainer->get('the-service-ID'); - - // ... - } - Mocking Services ~~~~~~~~~~~~~~~~ @@ -149,7 +119,7 @@ TODO .. _functional-tests: Application Tests ----------------- +----------------- Application tests check the integration of the different layers of an application (from the routing to the views). They are no different from unit From a17bd8a5aa90cdfc34ae21c55bb90c0b7feef2d5 Mon Sep 17 00:00:00 2001 From: Nyholm Date: Tue, 2 Mar 2021 19:40:37 +0100 Subject: [PATCH 5/5] Move around things --- testing.rst | 100 ++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/testing.rst b/testing.rst index 63a56a8dbfa..63151227b95 100644 --- a/testing.rst +++ b/testing.rst @@ -99,49 +99,42 @@ Symfony tests have access to a special container that includes both the public services and the non-removed :ref:`private services ` services:: - public function testSomething() - { - // this call is needed; otherwise the container will be empty - self::bootKernel(); - - $container = self::$container; - $someService = $container->get('the-service-ID'); - - $result = $someService->something(); - $this->assertTrue($result); - } + // tests/Service/AcmeServiceTest.php + namespace App\Tests\Service; -Mocking Services -~~~~~~~~~~~~~~~~ - -TODO - -.. _functional-tests: + use App\Service\AcmeService; + use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -Application Tests ------------------ + class AcmeServiceTest extends KernelTestCase + { + public function testSomething() + { + // this call is needed; otherwise the container will be empty + self::bootKernel(); -Application tests check the integration of the different layers of an -application (from the routing to the views). They are no different from unit -tests as far as PHPUnit is concerned, but they have a very specific workflow: + $container = self::$container; + $someService = $container->get(AcmeService::class); -* Make a request; -* Click on a link or submit a form; -* Test the response; -* Rinse and repeat. + $result = $someService->something(); + $this->assertTrue($result); + } + } -Before creating your first test, install the ``symfony/test-pack`` which -requires multiple packages providing some of the utilities used in the -tests: +.. tip:: -.. code-block:: terminal + To run your application tests, the ``KernelTestCase`` class needs to know which + is the application kernel to bootstrap it. The kernel class is usually + defined in the ``KERNEL_CLASS`` environment variable (included in the + default ``.env.test`` file provided by Symfony Flex): - $ composer require --dev symfony/test-pack + If your use case is more complex, you can also override the + ``createKernel()`` or ``getKernelClass()`` methods of your functional test, + which take precedence over the ``KERNEL_CLASS`` env var. Set-up your Test Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The Client used by application tests creates a Kernel that runs in a special +The tests creates a Kernel that runs in a special ``test`` environment. Since Symfony loads the ``config/packages/test/*.yaml`` in the ``test`` environment, you can tweak any of your application's settings specifically for testing. @@ -189,7 +182,7 @@ You can also use a different environment entirely, or override the default debug mode (``true``) by passing each as options to the ``createClient()`` method:: - $client = static::createClient([ + self::bootKernel([ 'environment' => 'my_test_env', 'debug' => false, ]); @@ -253,7 +246,7 @@ that ensures that each test is run with the same unmodified database: $ composer require --dev dama/doctrine-test-bundle -Now, enable it as a PHPUnit extension or listener: +Now, enable it as a PHPUnit extension: .. code-block:: xml @@ -326,8 +319,32 @@ Empty the database and reload *all* the fixture classes with: For more information, read the `DoctrineFixturesBundle documentation`_. -Write Your First Functional Test -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _functional-tests: + +Application Tests +----------------- + +Application tests check the integration of all the different layers of the +application (from the routing to the views). They are no different from unit tests +or integration tests as far as PHPUnit is concerned, but they have a very specific +workflow: + +* Make a request; +* Click on a link or submit a form; +* Test the response; +* Rinse and repeat. + +Before creating your first test, install the ``symfony/test-pack`` which +requires multiple packages providing some of the utilities used in the +tests: + +.. code-block:: terminal + + $ composer require --dev symfony/test-pack + + +Write Your First Application Test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Application tests are PHP files that typically live in the ``tests/Controller`` directory of your application. If you want to test the pages handled by your @@ -353,17 +370,6 @@ As an example, a test could look like this:: } } -.. tip:: - - To run your application tests, the ``WebTestCase`` class needs to know which - is the application kernel to bootstrap it. The kernel class is usually - defined in the ``KERNEL_CLASS`` environment variable (included in the - default ``.env.test`` file provided by Symfony): - - If your use case is more complex, you can also override the - ``createKernel()`` or ``getKernelClass()`` methods of your functional test, - which take precedence over the ``KERNEL_CLASS`` env var. - In the above example, you validated that the HTTP response was successful. The next step is to validate that the page actually contains the expected content. The ``createClient()`` method returns a client, which is like a browser that