Let's talk about how to stop copy-paste between projects and transfer the code to a re-usable plug-in bundle Symfony 5. A series of articles summarizing my experience with bundles will carry out in practice from creating a minimal bundle and refactoring a demo application to tests and the bundle release cycle.
In the previous part, we created the bundle configuration . In this article, we will analyze how to test a bundle, write some tests and create a micro application inside the bundle to run them.
If you are not consistently completing the tutorial, then download the application from the repository and switch to the 5-configuration branch .
Instructions for installing and starting the project in a file README.md
. You will find the final version of the code for this article in the 6-testing branch .
Porting tests to a bundle
Before refactoring, 2 tests were already written: a unit test of one of the services and a functional test of the controller.
We have a primitive application in which the infrastructure layer (DB) is not separated from the domain logic: tests use a temporary sqlite database.
Check if the tests work:
./vendor/bin/simple-phpunit
Error: Class 'App\Service\EventExporter\Exporters\GoogleCalendarExporter' not found
The tests left the namespace names App\
remaining from the code until it was transferred to the bundle.
In both tests, fix the namespace on bravik\CalendarBundle
and run it again.
Tests must pass successfully.
In the root of the bundle, create a folder tests
( bundles/CalendarBundle/tests
) and move the tests/Service
unit test folder there . We Controller
transfer the functional test in the folder a little later.
Running unit tests from a bundle
To run the test, we need to install PHPUnit inside the bundle:
cd bundles/CalendarBundle
composer require symfony/phpunit-bridge --dev
This will add the PHP Unit as a dev dependency to the composer.json
bundle, and also create a file composer.lock
. We donβt need it in bundles: create a .gitignore
file and add it there.
PHP Unit out of the box will not work. You need to configure it: specify where the tests lie and connect the composer autoloader.
- phpunit.xml.dist
.
, Symfony-, 1 bootstrap
:
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="bin/.phpunit/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="./vendor/autoload.php"
>
bootstrap
composer, PHPUnit .
:
./vendor/bin/simple-phpunit
!
: - .
β¦ ?
.
, 2 .
, β , β -.
.
, ,
-.
- Symfony β Kernel
symfony/http-kernel
.
, , .
. , , Symfony DI-, . symfony/framework-bundle
, http-kernel
.
composer require symfony/framework-bundle
tests/App
TestingKernel
.
Symfony\Component\HttpKernel\Kernel
, :
<?php
namespace bravik\CalendarBundle\Tests\App;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
class TestingKernel extends Kernel
{
public function __construct()
{
parent::__construct('test', false);
}
public function registerBundles()
{
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
}
}
composer bravik\CalendarBundle\Tests\App
.
: 'test'
, . , .
registerBundles()
. :
public function registerBundles()
{
return [
new CalendarBundle()
];
}
registerContainerConfiguration()
DI-.
.
symfony/router
. , .
. src/Kernel
-:
class Kernel extends BaseKernel
{
use MicroKernelTrait;
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
}
}
, Symfony- «» MicroKernelTrait
.
registerContainerConfiguration()
,
-:
configureContainer()
configureRoutes()
, .
:
TestingKernel
registerContainerConfiguration()
,use MicroKernelTrait;
- .
configureRoutes()
config/routes.yaml
:
protected function configureRoutes(RouteCollectionBuilder $routes)
{
$routes->import(__DIR__.'/../../config/routes.yaml');
}
yaml
, :
composer require symfony/yaml
.
?
tests/Controller/EventControllerTest
.
HTTP . HTTP-. , .
Symfony Request
, . index.php
:
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
, .
, Symfony HttpKernelBrowser
:
composer require symfony/browser-kit --dev
:
public static function createClient()
{
$kernel = new TestingKernel();
return new HttpKernelBrowser($kernel);
}
public function testSomeAction()
{
$client = static::createClient();
$response = $client->request("/some/action");
}
HttpKernelBrowser
. $client->request()
.
, , WebTestCase
, Symfony. createClient()
, , createKernel()
, .
, , β . KERNEL_CLASS
phpunit.xml.dist
:
<php>
<server name="APP_ENV" value="test" force="true" />
<server name="KERNEL_CLASS" value="bravik\CalendarBundle\Tests\App\TestingKernel"
force="true" />
</php>
:
./vendor/bin/simple-phpunit tests/Controller/EventControllerTest.php
...
LogicException: Container extension "framework" is not registered
MicroKernelTrait
. DI- Β«-Β», framework
.
FrameworkBundle
. :
public function registerBundles()
{
return [
new FrameworkBundle(),
new CalendarBundle()
];
}
:
InvalidArgumentException: Cannot determine controller argument for "bravik\CalendarBundle\Controller\EditorController::new()": the $entityManager argument is type-hinted with the non-existent class or interface: "Doctrine\ORM\EntityManagerInterface".
- ? EditorController
?
new CalendarBundle()
TestingKernel
, services.yaml
, .
autowiring Symfony . typehints , Symfony typehints .
, , TestingKernel
.
:
composer require doctrine/orm doctrine/doctrine-bundle symfony/twig-bundle
composer require doctrine/doctrine-fixtures-bundle liip/test-fixtures-bundle --dev
TestingKernel
:
public function registerBundles()
{
return [
new DoctrineBundle(),
new DoctrineFixturesBundle(),
new LiipTestFixturesBundle(),
new TwigBundle(),
];
}
: tests/App/config/config.yaml
:
#
# @see https://symfony.com/doc/current/reference/configuration/framework.html#test
framework:
test: true
doctrine:
# SQLITE var/test.db
dbal:
driver: pdo_sqlite
path: "%kernel.cache_dir%/test.db"
# ORM-
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
calendar:
enable_soft_delete: true
services:
# Twig, mock- webpack encore,
#
bravik\CalendarBundle\Tests\App\TwigWebpackSuppressor:
tags: ['twig.extension']
#
bravik\CalendarBundle\Tests\Fixtures\:
resource: '../../Fixtures'
tags: ['doctrine.fixture.orm']
, packages
, . Framework
, Doctrine
, .
TestingKernel
:
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config.yaml', 'yaml');
}
(!) , , var
Symfony . .gitignore
.
, dev
. - , β !
, - !
, , .
, Symfony - .
You will find the final version of the code for this article in the 6-testing branch .
In the next article we will talk about how to release new bundle releases without pain and suffering, how to organize the process of initial installation and updating of the bundle.
Other articles in the series:
Part 1. The minimum bundle
Part 2. We take out the code and templates in the bundle
Part 3. Integration of the bundle with the host: templates, styles, JS
Part 4. Interface for expanding the bundle
Part 5. Parameters and configuration
Part 6. Testing, microapplication inside the bundle
Part 7 Release cycle, installation and update