kosmosafive/bitrix-tests

Набор инструментов для тестирования Bitrix Framework

Maintainers

👁 kosmosafive

Package info

github.com/kosmosafive/bitrix-tests

pkg:composer/kosmosafive/bitrix-tests

Statistics

Installs: 154

Dependents: 0

Suggesters: 0

Stars: 4

Open Issues: 0

2.1.2 2026-01-15 09:29 UTC

Apache-2.0 50b71c97cf4a6d113fc03d8025cced246959a894

testingphpunittestunitbitrixpestInfectionkosmos

This package is auto-updated.

Last update: 2026-06-15 10:21:19 UTC


README

Решение обеспечивает запуск тестов для 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

Настройка

  1. Создать директорию для конфигурации тестов (например, local/tests). Перейти в созданную директорию.

  2. Создать файл настроек .env со следующей конфигурацией:

Опция Значение Пример
SITE_ID Идентификатор сайта s1
LANGUAGE_ID Идентификатор языка ru
LOG_LEVEL Уровень логирования, PSR-3 error
  1. Создать директорию для файлов, которые будут использоваться в тестах (например, local/tests/.data).

  2. Создать файл 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();
  1. Конфигурация 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.

  1. Конфигурация 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.

  1. Конфигурация 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'])
;

Миграция