Написання інтеграційних тестів для 1С-Бітрікса
Ви деплоїте оновлення, та через годину виявляєте, що імпорт з 1С перестав створювати нові товари — змінилася сигнатура методу в модулі catalog, а код інтеграції не був оновлений. Інтеграційні тести вирішують саме цю проблему: вони перевіряють, що ваш код коректно взаємодіє з ядром Бітрікса, БД та зовнішніми системами. Не юнит-тести у вакуумі, а реальні сценарії з реальним ядром.
Чим інтеграційні тести відрізняються від юнит-тестів у контексті Бітрікса
Юнит-тести Бітрікс-проекту безполезні без моків половини ядра. Клас, що викликає CIBlockElement::GetList(), залежить від інфоблоків, БД, кеша, прав доступу. Мокувати все це — писати другий Бітрікс. Інтеграційні тести завантажують реальне ядро, працюють з реальною (тестовою) БД та перевіряють повний цикл.
Типові сценарії:
- Створення замовлення через
CSaleOrder::Add()/\Bitrix\Sale\Order::create()— перевірка, що всі обробники подій спрацьовують, скидки застосовуються, статус коректний - Імпорт товарів з XML — перевірка маппінгу полів, створення розділів, оновлення цін
- Формування виконки для 1С — перевірка структури XML, коректності даних
- Обробка вебхука від платіжної системи — перевірка зміни статусу замовлення
Налаштування тестового окружения
PHPUnit + ядро Бітрікса. Бітрікс не поставляється з тестовою інфраструктурою, її потрібно налаштувати вручну.
Файл bootstrap.php для PHPUnit:
$_SERVER['DOCUMENT_ROOT'] = '/path/to/site';
$_SERVER['HTTP_HOST'] = 'test.local';
$_SERVER['SERVER_NAME'] = 'test.local';
$GLOBALS['DBType'] = 'mysql'; // або pgsql
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_NO_ACCELERATOR_RESET', true);
define('STOP_STATISTICS', true);
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
Константи NO_KEEP_STATISTIC та STOP_STATISTICS вимикають запис статистики, NOT_CHECK_PERMISSIONS — перевірку прав (інакше тести будуть залежати від поточного користувача).
Тестова БД. Два підходи:
- Окрема БД — копія продакшн-схеми без даних. Безпечно, але вимагає підтримки синхронізації схеми.
-
Транзакції — кожен тест оборачується в транзакцію, яка откатується в
tearDown(). Швидко, але не працює для тестів, які самі використовують транзакції (вкладені транзакції в MySQL ведуть себе неочевидно).
Рекомендований підхід для Бітрікса — транзакції з фоллбеком на ручну очистку:
protected function setUp(): void
{
\Bitrix\Main\Application::getConnection()->startTransaction();
}
protected function tearDown(): void
{
\Bitrix\Main\Application::getConnection()->rollbackTransaction();
}
Структура тестів
Розмішуйте тести у /local/tests/ з дзеркальною структурою:
/local/tests/
Integration/
Catalog/
ImportTest.php → тести імпорту товарів
PriceCalculationTest.php
Sale/
OrderCreationTest.php
DiscountTest.php
Exchange/
OneCExportTest.php
bootstrap.php
phpunit.xml
phpunit.xml:
<phpunit bootstrap="bootstrap.php">
<testsuites>
<testsuite name="Integration">
<directory>Integration</directory>
</testsuite>
</testsuites>
</phpunit>
Написання тестів: паттерни для Бітрікса
Тест створення замовлення:
public function testOrderCreationWithDiscount(): void
{
// Arrange: створюємо тестовий товар та скидку
$productId = $this->createTestProduct('TEST-001', 1000);
$discountId = $this->createTestDiscount(10); // 10%
// Act: створюємо замовлення
$order = \Bitrix\Sale\Order::create('s1', 1);
$basket = \Bitrix\Sale\Basket::create('s1');
$item = $basket->createItem('catalog', $productId);
$item->setFields(['QUANTITY' => 1, 'CURRENCY' => 'RUB', 'PRODUCT_PROVIDER_CLASS' => '\CCatalogProductProvider']);
$order->setBasket($basket);
$order->doFinalAction(true);
$result = $order->save();
// Assert
$this->assertTrue($result->isSuccess());
$this->assertEquals(900, $order->getPrice()); // 1000 - 10%
}
Тест імпорту XML:
Не викликайте CIBlockCMLImport напрямку — він важкий та погано контролюється. Тестуйте ваш код-обертку, який викликає API Бітрікса:
public function testProductImportCreatesElement(): void
{
$importer = new \Project\Import\ProductImporter(CATALOG_IBLOCK_ID);
$result = $importer->import([
'XML_ID' => 'TEST-IMPORT-001',
'NAME' => 'Тестовий товар',
'PRICE' => 500,
]);
$this->assertTrue($result->isSuccess());
$element = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'XML_ID' => 'TEST-IMPORT-001'],
false, false, ['ID', 'NAME']
)->Fetch();
$this->assertNotFalse($element);
$this->assertEquals('Тестовий товар', $element['NAME']);
}
Обробка подій у тестах
Обробники подій Бітрікса (OnBeforeIBlockElementAdd, OnSaleOrderBefore та ін.) — джерело неочікуваної поведінки у тестах. Обробник, зареєстрований у init.php, спрацьовує та у тестовому окруженні.
Два підходи:
- Прийняти як данність — тест перевіряє систему цілком, включаючи обробники. Це правильніше з точки зору інтеграційного тестування.
-
Тимчасово вимкнути —
RemoveEventHandler()уsetUp(), відновити уtearDown(). Використовуйте, коли обробник викликає зовнішній сервіс (надсилання email, запит до API).
Що не варто тестувати інтеграційно
- Вёрстку та візуальне відображення — задача для E2E-тестів (Playwright, Selenium)
- Чисту бізнес-логіку без залежностей від Бітрікса — задача юнит-тестів
- Продуктивність — інтеграційні тести повільні по визначенню, для бенчмарків використовуйте окремий фреймворк
Строки написання тестів
| Покриття | Обсяг | Строк |
|---|---|---|
| Критичний шлях (оформлення замовлення, імпорт) | 15-25 тестів | 3-5 днів |
| Основна функціональність | 50-80 тестів | 1-2 тижні |
| Розширене покриття (edge cases, помилки) | 100+ тестів | 3-4 тижні |
Почніть з тестів на місця, де найчастіше ломиться. Звичайно це імпорт/експорт 1С та розрахунок цін з урахуванням скидок.







