Wie kann man Code mit Symfony 5-Bundles wiederverwenden? Teil 6. Testen

Lassen Sie uns darüber sprechen, wie Sie das Kopieren und Einfügen zwischen Projekten stoppen und den Code in ein wiederverwendbares Plug-In-Bundle Symfony 5 übertragen können. Eine Reihe von Artikeln, die meine Erfahrungen mit Bundles zusammenfassen, wird in der Praxis von der Erstellung eines minimalen Bundles über die Umgestaltung einer Demo-Anwendung bis hin zu Tests und dem Bundle-Release-Zyklus durchgeführt.


Im vorherigen Teil haben wir die Bundle-Konfiguration erstellt . In diesem Artikel analysieren wir, wie ein Bundle getestet, einige Tests geschrieben und eine Mikroanwendung innerhalb des Bundles erstellt wird, um sie auszuführen.


Serieninhalt

Wenn Sie das Lernprogramm nicht konsequent abschließen, laden Sie die Anwendung aus dem Repository herunter und wechseln Sie zum Zweig mit 5 Konfigurationen .


Anweisungen zum Installieren und Starten des Projekts in einer Datei README.md. Die endgültige Version des Codes für diesen Artikel finden Sie im 6-Test- Zweig .


Portierungstests in ein Bundle


Vor dem Refactoring wurden bereits 2 Tests geschrieben: ein Komponententest eines der Dienste und ein Funktionstest des Controllers.


Wir haben eine primitive Anwendung, in der die Infrastrukturschicht (DB) nicht von der Domänenlogik getrennt ist: Tests verwenden eine temporäre SQLite-Datenbank.


Überprüfen Sie, ob die Tests funktionieren:


./vendor/bin/simple-phpunit

Error: Class 'App\Service\EventExporter\Exporters\GoogleCalendarExporter' not found

Bei den Tests blieben die Namespace-Namen App\im Code, bis sie in das Bundle übertragen wurden.


Korrigieren Sie in beiden Tests den Namespace bravik\CalendarBundleund führen Sie ihn erneut aus.
Tests müssen erfolgreich bestanden werden.


Erstellen Sie im Stammverzeichnis des Bundles einen Ordner tests( bundles/CalendarBundle/tests) und verschieben Sie den tests/ServiceUnit-Test- Ordner dorthin . ControllerDen Funktionstest übertragen wir etwas später in den Ordner .


Ausführen von Komponententests aus einem Bundle


Um den Test auszuführen, müssen wir PHPUnit im Bundle installieren:


cd bundles/CalendarBundle
composer require symfony/phpunit-bridge --dev

Dadurch wird die PHP-Einheit als Entwicklungsabhängigkeit zum composer.jsonBundle hinzugefügt und eine Datei erstellt composer.lock. Wir brauchen es nicht in Bundles: Erstellen Sie eine .gitignoreDatei und fügen Sie sie dort hinzu.


Die sofort einsatzbereite PHP-Einheit funktioniert nicht. Sie müssen es konfigurieren: Geben Sie an, wo die Tests liegen, und verbinden Sie den 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()
    {
        // TODO: Implement registerBundles() method.
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        // TODO: Implement registerContainerConfiguration() method.
    }
}

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");
    // Assertion on response
    // ...
}

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 - .


Die endgültige Version des Codes für diesen Artikel finden Sie im 6-Test- Zweig .


Im nächsten Artikel werden wir darüber sprechen, wie neue Bundle-Releases ohne Schmerzen und Leiden veröffentlicht werden und wie der Prozess der Erstinstallation und Aktualisierung des Bundles organisiert wird.


Andere Artikel in der Reihe:


Teil 1. Das minimale Bundle
Teil 2. Wir nehmen den Code und die Vorlagen im Bundle heraus.
Teil 3. Integration des Bundles mit dem Host: Vorlagen, Stile, JS
Teil 4. Schnittstelle zum Erweitern des Bundles
Teil 5. Parameter und Konfiguration
Teil 6. Testen, Mikroanwendung innerhalb des Bundles
Teil 7 Release-Zyklus, Installation und Update


All Articles