Тестирование приложений
Приложения на Pimcore можно тестировать с помощью любого решения для тестирования PHP, но на данной странице представлены два жизнеспособных подхода:
- Стандартная настройка тестирования Symfony с использованием PHPUnit
- Codeception (основан на PHPUnit) для более продвинутых функций, таких как тестирование в браузере через Selenium, благодаря системе модулей Codeception.
В целом рекомендуется начинать с первого подхода, так как его проще настроить и использовать для старта. Однако стоит учесть, что настройка PHPUnit «из коробки» не включает готового решения для подготовки приложения к тестам (например, обеспечения идентичного набора данных при каждом запуске), поэтому это остается на ваше усмотрение. Вы можете подготовить тестовые данные в файле bootstrap или запустить скрипт перед началом выполнения тестов.
В дополнение к общим функциям Codeception, модули Pimcore для Codeception предоставляют набор помощников (helpers) для развертывания Pimcore с «нуля». Модуль Pimcore умеет удалять и заново создавать базу данных, а дополнительные модули, такие как ClassManager, предоставляют код для создания классов Pimcore из JSON-экспортов. Поскольку инициализация БД настраивается, вы можете использовать модуль так, как вам нужно (например, инициализировать приложение самостоятельно или просто запускать тесты без логики инициализации БД). Примеры использования этих модулей можно найти в настройках тестов Pimcore.
PHPUnit
Поскольку Pimcore является стандартным приложением Symfony, вы можете использовать настройку PHPUnit точно так же, как описано в документации по тестированию Symfony. Все, что вам нужно — это создать пользовательский файл bootstrap, чтобы процесс запуска Pimcore имел все необходимое. Начните с добавления Symfony PHPUnit bridge в ваш проект:
$ composer require --dev 'symfony/phpunit-bridge:*'
Вместе с symfony/phpunit-bridge поставляется файл vendor/bin/simple-phpunit, который использует свою версию PHPUnit. Чтобы simple-phpunit использовал правильную версию, исключите phpunit из карты классов автозагрузчика и обновите его командой composer dump-autoload -o:
"autoload": {
...
"exclude-from-classmap": [
"vendor/phpunit"
]
}
Затем создайте файл конфигурации phpunit.xml.dist в корне проекта. Приведенный ниже конфигурационный файл
ожидает ваши тесты в каталоге tests/ и обрабатывает файлы в каталоге src/ при расчете отчетов о покрытии кода.
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.4/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
<filter>
<allowlist processUncoveredFilesFromAllowlist="true">
<directory suffix=".php">src</directory>
</allowlist>
</filter>
<php>
<env name="SYMFONY_PHPUNIT_VERSION" value="7.4" />
</php>
</phpunit>
Теперь мы готовы написать первый тест. Предполагая, что у нас есть класс App\Calculator, в котором есть метод add(int $a, int $b): int
, добавьте тест в tests/App/CalculatorTest.php. Нет необходимости, но рекомендуется использовать структуру каталогов, аналогичную
структуре кода вашего приложения в вашем тестовом каталоге.
<?php
// tests/App/CalculatorTest.php
namespace Tests\App;
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
private Calculator $calculator;
protected function setUp(): void
{
$this->calculator = new Calculator();
}
public function testAdd(): void
{
$this->assertEquals(15, $this->calculator->add(10, 5));
}
/**
* @dataProvider addDataProvider
*/
public function testAddWithProvider(int $a, int $b, int $expected): void
{
$this->assertEquals($expected, $this->calculator->add($a, $b));
}
public function addDataProvider(): array
{
return [
[1, 2, 3],
[10, 5, 15],
[-5, 5, 0],
[5, -5, 0],
[0, 10, 10],
[-50, -50, -100],
[-50, 10, -40]
];
}
}
Это все, что вам нужно для написания простых модульных тестов, которые не зависят от среды Pimcore. Просто запустите тесты с помощью PHPUnit-оболочки Symfony:
$ vendor/bin/simple-phpunit
PHPUnit 7.4.5 by Sebastian Bergmann and contributors.
Testing default
........ 8 / 8 (100%)
Time: 174 ms, Memory: 10.00MB
OK (8 tests, 8 assertions)
Загрузка (Bootstrapping) Pimcore
Если вы хотите писать тесты, затрагивающие объекты Pimcore или контейнер Symfony (например, функциональные тесты контроллеров), необходимо убедиться, что Pimcore правильно инициализирован перед запуском. Измените файл конфигурации, указав путь к пользовательскому bootstrap файлу и добавив переменные окружения:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.4/phpunit.xsd"
bootstrap="tests/bootstrap.php"
colors="true">
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
<filter>
<allowlist processUncoveredFilesFromAllowlist="true">
<directory suffix=".php">src</directory>
</allowlist>
</filter>
<php>
<!-- отрегулируйте по мере необходимости -->
<env name="SYMFONY_PHPUNIT_VERSION" value="7.4" />
<env name="PIMCORE_PROJECT_ROOT" value="." />
<env name="KERNEL_DIR" value="app" />
<env name="KERNEL_CLASS" value="AppKernel" />
</php>
</phpunit>
В приведенном выше примере требуется файл tests/bootstrap.php, который выполняется перед запуском тестов. Создайте загрузочный файл
со следующим содержимым (настройте по мере необходимости):
<?php
// tests/bootstrap.php
include "../../vendor/autoload.php";
\Pimcore\Bootstrap::setProjectRoot();
\Pimcore\Bootstrap::bootstrap();
Теперь мы готовы писать тесты, которые зависят от окружения. Symfony уже предоставляет KernelTestCase и
WebTestCase в качестве базовых классов для тестов с использованием контейнера, но Pimcore ожидает, что контейнер будет установлен через Pimcore::setContainer()
после начальной загрузки. Это делается автоматически, если вы используете Pimcore\Test\KernelTestCase и Pimcore\Test\WebTestCase
в качестве базовых классов, в противном случае вам нужно обязательно перезаписать createKernel и установить контейнер в классе Pimcore.
Давайте создадим функциональный тест, который проверит реакцию контроллера (подробности смотрите в документации по тестированию Symfony). В
приведенном ниже примере предполагается установка с использованием профиля установки demo-basic.
<?php
// tests/App/Controller/ContentControllerTest.php
declare(strict_types=1);
namespace Tests\App\Controller;
use Pimcore\Test\WebTestCase;
class ContentControllerTest extends WebTestCase
{
public function testRedirectFromEn(): void
{
$client = static::createClient();
$client->request('GET', '/en');
$this->assertTrue($client->getResponse()->isRedirect());
$client->followRedirect();
$this->assertEquals('/', $client->getRequest()->getPathInfo());
}
public function testPortal(): void
{
$client = static::createClient();
$crawler = $client->request('GET', '/');
$response = $client->getResponse();
$this->assertTrue($response->isSuccessful(), 'response status is 2xx');
$this->assertTrue($response->headers->contains('X-Custom-Header', 'Foo'));
$this->assertTrue($response->headers->contains('X-Custom-Header', 'Bar'));
$this->assertTrue($response->headers->contains('X-Custom-Header2', 'Bazinga'));
$this->assertEquals(
1,
$crawler->filter('h1:contains("Ready to be impressed?")')->count()
);
}
}
Если бы вы запустили набор тестов сейчас, он завершился бы ошибкой со списком ошибок, поскольку тест не может подключиться к базе данных. Это
связано с тем, что тесты выполняются в среде test, и эта среда настроена на использование другого подключения к базе
данных, которое по умолчанию определено как переменная среды PIMCORE_TEST_DB_DSN (см. config/packages/test/config.yaml).
Вы можете либо определить DSN базы данных как переменную среды в своей оболочке, жестко запрограммировать ее в конфигурационном файле PHPUnit (не рекомендуется), либо полностью удалить/изменить настраиваемый раздел doctrine из config/packages/test/config.yaml.
То, что использовать, во многом зависит от вашей среды и ваших тестов - если у вас есть тесты, которые вносят изменения в базу данных, вы, вероятно, захотите запустить их в другой базе данных с заранее определенным набором данных. В приведенном ниже примере соединение с БД передается просто как переменная env:
$ PIMCORE_TEST_DB_DSN="mysql://username:password@localhost/pimcore" vendor/bin/simple-phpunit
PHPUnit 7.4.5 by Sebastian Bergmann and contributors.
Testing default
.......... 10 / 10 (100%)
Time: 2.69 seconds, Memory: 36.00MB
OK (10 tests, 15 assertions)
Для получения дополнительной информации вы можете ознакомиться с Документацией по тестированию Symfony. Просто не забудьте убедиться, что Pimcore правильно загружен перед запуском тестов.
Codeception
Для основных тестов Pimcore использует Codeception, который обертывает PHPUnit и добавляет множество приятных функций, особенно для
организации тестов и добавления вспомогательного кода, который можно использовать из тестов. В принципе, вы можете использовать те же настройки,
что и в ядре Pimcore, определив пользовательский набор тестов и воспользовавшись основными помощниками Pimcore. Наиболее важным помощником является \Pimcore\Tests\Support\Helper\Pimcore, который расширяет [модуль Symfony] Codeception(https://codeception.com/docs/modules/Symfony) для функционального тестирования и добавляет логику для начальной загрузки Pimcore и удаления / повторного создания базы данных и каталога классов в пустой установке, чтобы каждый
набор тестов запускался с чистой установки.
ВНИМАНИЕ: Программа настройки codeception в Pimcore предназначена для использования в CI, где база данных и структуры данных создаются из пустой установки. Это означает, что, если не настроено иначе, помощники codeception удалят базу данных и создадут ее заново, а также УДАЛЯТ файлы классов. Используйте с осторожностью и только при тестовой настройке!
Чтобы начать, добавьте файл tests/codeception.dist.yml для вашей пользовательской настройки тестирования, который определяет каталоги и базовое
поведение:
# tests/codeception.dist.yml
namespace: Tests
support_namespace: Support
actor_suffix: Tester
paths:
tests: .
output: ./_output
data: ./Support/Data
support: ./Support
envs: ./_envs
settings:
bootstrap: _bootstrap.php
colors: true
params:
- env
extensions:
enabled:
- Codeception\Extension\RunFailed
Pimcore уже поставляет файл codeception.dist.yml, который настроен для запуска основных тестов Pimcore. Возможно, вы захотите изменить его
, чтобы по умолчанию запускать свои собственные настройки тестирования:
# codeception.dist.yml
settings:
memory_limit: 1024M
colors: true
paths:
output: var/log
include:
- tests
Вы можете создать любое количество наборов тестов в Codeception. Чтобы соответствовать приведенному выше примеру с PHPUnit, мы создадим 2 набора тестов
unit и functional для модульного и функционального тестирования. Следующие команды должны создать базовую структуру каталогов/файлов
в tests/:
$ vendor/bin/codecept -c tests/codeception.dist.yml generate:suite unit
$ vendor/bin/codecept -c tests/codeception.dist.yml generate:suite functional
В приведенном выше файле конфигурации содержится ссылка на файл _bootstrap.php. Создайте файл tests/_bootstrap.php со следующим содержимым
, чтобы убедиться, что Pimcore может быть загружен во время тестов. Настройте в соответствии с вашими потребностями.
<?php
// tests/_bootstrap.php
use Pimcore\Tests\Support\Util\Autoloader;
// определите корень проекта, который будет использоваться на протяжении всего процесса начальной загрузки
define('PIMCORE_PROJECT_ROOT', realpath(__DIR__ . '/..'));
// установите используемую среду pimcore/symfony
putenv('APP_ENV=test');
require_once PIMCORE_PROJECT_ROOT . '/vendor/autoload.php';
\Pimcore\Bootstrap::setProjectRoot();
\Pimcore\Bootstrap::bootstrap();
\Pimcore\Bootstrap::kernel();
// добавьте основную тестовую библиотеку pimcore в автозагрузчик - это также можно сделать в разделе autoload-dev composer.json
// но это сделано здесь в демонстрационных целях
require_once PIMCORE_PROJECT_ROOT . '/vendor/pimcore/pimcore/tests/Support/Util/Autoloader.php';
Autoloader::addNamespace('Pimcore\Tests\Support', PIMCORE_PROJECT_ROOT . '/vendor/pimcore/pimcore/tests/Support');
Настройки из tests/unit.suite.yml должны подойти для стандартной настройки модульного тестирования без зависимостей, но нам нужно изменить
функциональный набор тестов, чтобы инициализировать тестовую базу данных и загрузить ядро Pimcore перед запуском тестов. Настройте
пакет для использования вспомогательного средства \Pimcore\Tests\Support\Helper\Pimcore:
# tests/functional.suite.yml
actor: FunctionalTester
modules:
enabled:
- \Tests\Support\Helper\Functional
- \Pimcore\Tests\Support\Helper\Pimcore:
# ВНИМАНИЕ: приведенная ниже конфигурация означает, что программа запуска тестирования
# удалит и заново создаст базу данных Pimcore и удалит var/classes
# использовать только в тестовой настройке (например, во время CI)!
connect_db: true
initialize_db: true
purge_class_directory: true
# Если значение равно true, то будут созданы структуры базы данных для всех определений
setup_objects: false
Это настроит функциональный тест, который отправит запрос непосредственно через ядро Symfony (аналогично настройке PHPUnit, описанной выше). Однако Codeception упрощает использование полноценного браузера для приемочного тестирования, настраивая дополнительные модули, такие как модуль WebDriver для тестирования на Selenium.
Давайте начнем писать тесты, добавив простой модульный тест:
<?php
// tests/unit/ExampleTest.php
namespace Tests\Unit\App;
use Codeception\Test\Unit;
/**
* Этот тест является всего лишь манекеном для демонстрационных целей
* и на самом деле не тестирует какой-либо класс.
*/
class ExampleTest extends Unit
{
/**
* Tester actor раскрывает методы, добавленные помощниками
*/
protected \Tests\UnitTester $tester;
public function testPhpCanCalculate(): void
{
$this->assertEquals(15, 10 + 5);
$this->assertEquals(100, pow(10, 2));
}
/**
* @dataProvider addDataProvider
*/
public function testPhpCanAddWithProvider(int $a, int $b, int $expected): void
{
$this->assertEquals($expected, $a + $b, sprintf('%d + %d = %d', $a, $b, $expected));
}
public function testSomethingElse(): void
{
$obj1 = new \stdClass();
$obj2 = new \stdClass();
$obj3 = new \stdClass();
$obj1->obj = $obj3;
$obj2->obj = $obj3;
$this->assertNotNull($obj1);
$this->assertNotNull($obj2);
$this->assertNotNull($obj3);
$this->assertNotSame($obj1, $obj2);
$this->assertSame($obj1->obj, $obj2->obj);
$this->assertSame($obj3, $obj1->obj);
$this->assertSame($obj3, $obj2->obj);
}
public function testException(): void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('This test is about to fail');
throw new \RuntimeException('This test is about to fail');
}
public function addDataProvider(): array
{
return [
[1, 2, 3],
[10, 5, 15],
[-5, 5, 0],
[5, -5, 0],
[0, 10, 10],
[-50, -50, -100],
[-50, 10, -40]
];
}
}
И функциональный тест, проверяющий пустую индексную страницу. Поскольку помощник Pimcore основан на модуле Codeception Symfony, вы
можете напрямую использовать тесты Symfony, такие как $I->amOnRoute().
<?php
// tests/functional/App/IndexPageCest.php
namespace Tests\Functional\App;
use Tests\FunctionalTester;
class IndexPageCest
{
public function testFrontpage(FunctionalTester $I): void
{
$I->amOnPage('/');
$I->canSeeResponseCodeIs(200);
$I->amOnRoute('document_1');
$I->seeElement('#site #logo a', ['href' => 'http://www.pimcore.com/']);
$I->seeElement('#site #logo img', ['src' => '/bundles/pimcoreadmin/img/logo-claim-gray.svg']);
}
}
Как и при установке PHPUnit, при тестировании по умолчанию предполагается, что подключение к базе данных будет указано как переменная env. Запустите новую тестовую установку, настроив DB DSN перед запуском codeception:
$ PIMCORE_TEST_DB_DSN="mysql://username:password@localhost/pimcore" vendor/bin/codecept run -c tests/codeception.dist.yml
Codeception PHP Testing Framework v2.3.8
Powered by PHPUnit 7.4.5 by Sebastian Bergmann and contributors.
[DB] Initializing DB pimcore5_test
[DB] Dropping DB pimcore5_test
[DB] Creating DB pimcore5_test
[DB] Successfully connected to DB pimcore5_test
[DB] Initialized the test DB pimcore5_test
[INIT] Purging class directory var/classes
Tests.functional Tests (1) --------------------------------------------------------------------------------------------------------------------------
Testing Tests.functional
â IndexPageCest: Test frontpage (3.23s)
-----------------------------------------------------------------------------------------------------------------------------------------------------
Tests.unit Tests (10) -------------------------------------------------------------------------------------------------------------------------------
â ExampleTest: Php can calculate (0.14s)
â ExampleTest: Php can add with provider | #0 (0.00s)
â ExampleTest: Php can add with provider | #1 (0.00s)
â ExampleTest: Php can add with provider | #2 (0.00s)
â ExampleTest: Php can add with provider | #3 (0.00s)
â ExampleTest: Php can add with provider | #4 (0.00s)
â ExampleTest: Php can add with provider | #5 (0.00s)
â ExampleTest: Php can add with provider | #6 (0.00s)
â ExampleTest: Something else (0.01s)
â ExampleTest: Exception (0.01s)
-----------------------------------------------------------------------------------------------------------------------------------------------------
Time: 6.96 seconds, Memory: 44.25MB
OK (11 tests, 21 assertions)
Вы можете предложить улучшение документации или задать вопрос в комментариях.
Если вам нужна полноценная консультация — вы можете заказать её на нашем сайте.