kosmosafive/bitrix-tests
| Install | |
|---|---|
composer require kosmosafive/bitrix-tests |
|
| Latest Version: | 2.1.2 |
| PHP: | ^8.3 |
| License: | Apache-2.0 |
| Last Updated: | Jan 15, 2026 |
| Links: | GitHub · Packagist |
Набор инструментов для тестирования Bitrix Framework
Решение обеспечивает запуск тестов для Bitrix Framework.
Поддерживаются:
Установка
Bitrix Framework в вашей инсталляции может не поддерживать 7ую версию symfony/console. Для инсталляции необходимо поднять зависимость в bitrix/composer-bx.json.
Если используются консольные команды, необходимо скорректировать классы команд. Например, в ядре указать тип возвращаемых данных (:int) у метода execute() в файлах:
- bitrix/modules/main/lib/cli/ormannotatecommand.php
- bitrix/modules/translate/lib/cli/indexcommand.php
Настройка
-
Создать директорию для конфигурации тестов (например, local/tests). Перейти в созданную директорию.
-
Создать файл настроек .env со следующей конфигурацией:
| Опция | Значение | Пример |
|---|---|---|
| SITE_ID | Идентификатор сайта | s1 |
| LANGUAGE_ID | Идентификатор языка | ru |
| LOG_LEVEL | Уровень логирования, PSR-3 | error |
-
Создать директорию для файлов, которые будут использоваться в тестах (например, local/tests/.data).
-
Создать файл bootstrap.php с содержимым:
<?php
declare(strict_types=1);
use Kosmosafive\Bitrix\Tests\Bootstrap;
$classLoader = require __DIR__ . '/../vendor/autoload.php';
/**
* Опционально можно сконфигурировать автозагрузку для архитектурного тестирования
*/
$autoload = [
['Vendor\Example\\', __DIR__ . '/../modules/vendor.example/lib'],
];
(new Bootstrap(
$classLoader,
dirname(__DIR__, 2),
__DIR__ . '/.env',
__DIR__ . '/.data',
$autoload
))->initialize();
- Конфигурация PHPUnit
Создать файл phpunit.xml.dist. Например,
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="bootstrap.php"
colors="true"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.3/phpunit.xsd"
cacheDirectory=".phpunit.cache"
executionOrder="default"
defaultTestSuite="coverage"
>
<testsuites>
<testsuite name="unit">
<directory>../modules/*/tests/Unit</directory>
</testsuite>
<testsuite name="integration">
<directory>../modules/*/tests/Integration</directory>
</testsuite>
<testsuite name="application">
<directory>../modules/*/tests/Application</directory>
</testsuite>
<testsuite name="architecture">
<directory>../modules/*/tests/Architecture</directory>
</testsuite>
<testsuite name="stress">
<directory>../modules/*/tests/Stress</directory>
</testsuite>
<testsuite name="coverage">
<directory>../modules/*/tests/Unit</directory>
<directory>../modules/*/tests/Integration</directory>
</testsuite>
</testsuites>
<source
restrictNotices="true"
>
<include>
<directory suffix=".php">../modules/*/lib</directory>
</include>
</source>
</phpunit>
Добавьте в .gitignore директории .phpunit.cache и coverage.
- Конфигурация Infection
Создать файл infection.json5. Например,
{
"$schema": "../vendor/infection/infection/resources/schema.json",
"source": {
"directories": [
"{modules/*/lib}"
]
},
"timeout": 10,
"logs": {
"text": "infection/infection.log",
"html": "infection/infection.html"
},
"mutators": {
"@default": true,
"@function_signature": false
},
"bootstrap": "tests/bootstrap.php",
"testFrameworkOptions": "--testsuite=unit"
}
Добавьте в .gitignore директорию infection.
- Конфигурация Pest
Создать файл Pest.php. По умолчанию пустой файл.
Структура тестов
Тесты группируются по модулям. В корневой директории модуля необходимо создать директорию tests. Далее директории и файлы именуются в CamelCase. Следующая директория выбирается исходя из типа теста:
- Unit — тесты отдельных классов \ функций. Должны гарантировать, что тестируемая единица соответствует заданной схеме поведения.
- Integration — тесты сценариев. Охватывают разом большую часть приложения в сравнении с unit-тестами. Могут использовать сервисы из контейнера, подключение к тестовой базе данных и т.д.
- Application — тесты приложения. Полноценно тестируют некоторый процесс. Могут работать со страницей сайта, с внешними сервисами.
- Architecture — архитектурное тестирование.
- Stress — нагрузочное тестирование.
Дальнейшая структура директорий \ файлов должна повторять таковую у модуля относительно директории lib. Соглашение об организации тестов для классов, расположенных вне директории lib, в настоящий момент не обсуждалось.
Namespace строится по следующей схеме: {module_name}\Tests{type}{path}. Например, Example\Main\Tests\Integration\Domain\Service.
Название класса: {class_name}Test. Например, ExampleServiceTest.
Файл теста
Unit: модульное тестирование
Класс теста наследует \Kosmosafive\Bitrix\Tests\PHPUnit\BitrixTestCase.
Реализуйте тесты для всех публичных методов класса. В качестве названия тестового метода используйте название оригинального метода с префиксом test, например getId → testGetId.
Для реализации проверок на предопределенном \ генерируемом множестве данных воспользуйтесь провайдером данных.
use PHPUnit\Framework\Attributes\DataProvider;
public static function exampleProvider(): array
{
return [
['id' => 1],
['id' => 2],
['id' => 3],
];
}
#[DataProvider('exampleProvider')]
public function testGetId(int $id): void
{
$entity = new Entity($id);
$this->assertEquals($id, $entity->getId());
}
Пример демонстрирует исключительно идею провайдера. Результатом запуска теста будет цикл по массиву данных провайдера с запуском тестового метода на каждой итерации. Суммарно три проверки.
Integration: интеграционное тестирование
Класс теста может наследовать \Kosmosafive\Bitrix\Tests\PHPUnit\BitrixTestCase, но рекомендуется наследовать \Kosmosafive\Bitrix\Tests\PHPUnit\Integration\TestCase.
Реализуйте тесты для всех публичных методов класса.
Если возникает необходимость протестировать поведение метода при разных состояниях приложения, разделите тестируемый метод на несколько, используя постфикс _{case}. Например, testGetList_User, testGetList_Manager.
Для более эффективного тестирования убедитесь, что у вас разделены контроллеры, логика и представление.
Старайтесь не раскрывать реализацию.
BitrixTestCase
Кастомные утверждения
- assertResultSuccess — в качестве аргумента принимает экземпляр \Bitrix\Main\Result.
Установка идентификатора текущего пользователя
Метод setUserId(?int $id = null): void позволяет установить \ удалить идентификатор текущего пользователя.
Например, при сохранении элемента в качестве идентификатора пользователя может использоваться идентификатор текущего пользователя.
Только устанавливается идентификатор. Это значит, что не будут вызваны события процесса авторизации пользователя.
Резервное копирование и восстановление глобальных переменных
По умолчанию перед запуском каждого теста восстанавливается SESSION до состояния до запуска теста.
Если есть необходимость расширить список глобальных переменных, необходимо реализовать метод getBackupGlobalsKeys(): array, возвращающий массив ключей глобальных переменных из GLOBALS.
Например, для SESSION ключом будет _SESSION.
Вызов метода до\после теста\класса теста
Для выполнения некоторой логики до\после теста\класса теста нет необходимости переопределять setUp() и т.д.
Можно воспользоваться атрибутами Before\BeforeClass и After\AfterClass.
Название метода при этом может быть любым, но рекомендуется в качестве префикса использовать название стандартного метода с аналогичным порядком вызова.
use PHPUnit\Framework\Attributes\Before;
class SameServiceTest extends TestCase
{
protected SameServiceInterface $service;
#[Before] protected function setUpService(): void
{
Loader::requireModule('example.main');
$this->service = ServiceLocator::getInstance()->get(SameServiceInterface::class);
}
}
Mockery
Одной из возможностей Mockery является возможность переопределения класса без необходимости непосредственной передачи к точке вызова (перезагрузка).
Например, в некотором сервисе есть метод получения детальной карточки сущности, в котором проверяются права доступа. Проверка при этом вызывается непосредственно в самом методе.
use Bitrix\Main\Result;
use Bitrix\Main\Error;
class SameService
{
public function getDetail($id): Result
{
$result = new Result();
if (!Access::canView($id)) {
return $result->addError(new Error('Access denied'));
}
return $result->setData(['entity' => $this->repository->get($id)]);
}
}
Мы можем перезагрузить класс Access следующим образом.
use Mockery;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
#[RunTestsInSeparateProcesses] class SameServiceTest extends TestCase
{
public function testGetDetail(): void
{
$access = Mockery::mock('overload:\Access');
$access->shouldReceive('canView')->once()->with(1)->andReturn(true);
$id = 1;
$result = $this->service->getDetail($id);
$this->assertResultSuccess($result);
}
}
Обратите внимание на
#[RunTestsInSeparateProcesses]. При использовании функционала перезагрузки тесты необходимо запускать раздельно. Не нужно добавлять, если тесты запускаются с помощью Pest.
Integration\TestCase
Тестовая база данных
Перед запуском теста проверяется наличие и полнота тестовой базы данных.
Если тестовая база данных не существует, она будет создана.
Если тестовая база данных существует, но количество таблиц и представлений (сумма) основной базы данных отличается от тестовой, тестовая будет пересоздана.
Если количество таблиц и представлений (сумма) совпадает, и запуск теста осуществлен пользователем в режиме, в котором он может ответить на вопрос в консоли, можно опционально пересоздать базу данных.
Если перед запуском тестов, работающих в "тихом" режиме, предполагает пересоздать тестовую базу данных, необходимо запустить тест в режиме, в котором есть возможность ответить на вопрос о пересоздании в консоли.
Тестовые данные (ORM)
Поддерживаются: пользовательские сущности, пользователи, файлы.
Поддерживаемые форматы данных: JSON.
JSON
ключи:
- className — имя класса
- data — массив данных
Пользовательская сущность
{
"className": "Example\\Main\\Same",
"data": [
{
"ID": 1,
"ACTIVE": true
},
{
"ID": 2,
"ACTIVE": false
}
]
}
Файлы
{
"className": "Bitrix\\Main\\FileTable",
"data": [
{
"name": "sample.pdf",
"type": "application/pdf",
"description": "",
"tmp_name": "1.pdf"
}
]
}
При указании пути до файла (tmp_name) можно указать полный или короткий путь.
Краткая форма записи предполагает поиск файла в директории local/tests/.data.
Пользователи
{
"className": "Bitrix\\Main\\UserTable",
"data": [
{
"LOGIN": "admin",
"PASSWORD": "Admin123456!@#",
"LID": "ru",
"ACTIVE": true,
"BLOCKED": false,
"DATE_REGISTER": "2020-01-01 00:00:00",
"EMAIL": "admin@local.local",
"NAME": "John",
"LAST_NAME": "Doe",
"SECOND_NAME": ""
}
]
}
Для указания списка файлов данных теста реализуйте метод getOrmDataFilenameList().
Путь до файла может быть указан явно или коротко.
Краткая форма записи предполагает только название файла.
Поиск файла в таком случае будет осуществляться от директории с классом по пути .seed/{className}.
Например,
tests\Integration\.seed\SameServiceTest\user.json
tests\Integration\SameServiceTest.php
Если в процессе работы с данными заполняются таблицы, которые впоследствии вам необходимо очистить, добавьте файл данных с пустым массивом данных.
Мутационное тестирование
Мутационное тестирование позволяет определить условный уровень качества ваших модульных (Unit) тестов.
Использование
Подробнее об установке и запуске можно прочитать в документации. В примере выполняется запуск из директории local с использованием Xdebug.
Оценка директории модуля
infection --configuration="tests/infection.json5" --filter=modules/example.module/lib/Domain/ --threads=max
Оценка класса
infection --configuration="tests/infection.json5" --filter=modules/example.module/lib/Domain/Example/Example.php --threads=max
Архитектурное тестирование
Ожидания
notToUseBannedFunctions(array $exclude = [])
Проверка на использование нежелательных функций. В параметре можно передать массив функций, которые использовать разрешено.
arch()
->expect('Vendor\Example')
->notToUseBannedFunctions(['unserialize'])
;