Вступ
На одному проєкті у сфері логістики модуль розрахунку доставки видавав некоректні тарифи після кожного оновлення — через відсутність unit-тестів. Після рефакторингу та написання тестів помилки зникли, а час на внесення змін скоротився вдвічі. Вартість регресій впала на 70%, а бюджет на підтримку модуля знизився вдвічі. Це типова ситуація: код Бітрікс-модулів часто щільно пов'язаний з ядром, що робить його складним для тестування. Досвід нашої команди в десятках проєктів підтверджує: правильна архітектура та unit-тести скорочують час розробки нових фіч вдвічі та знижують кількість багів на 70%. Ми регулярно стикаємося з проєктами, де бізнес-логіка не ізольована, і написання тестів вимагає попереднього рефакторингу. Однак навіть у таких випадках unit-тести окупаються протягом перших місяців експлуатації.
Чому unit-тести в Бітрікс складніші, ніж в інших проєктах?
Типові проблеми, які ми бачимо на старті:
- Щільна пов'язаність з ядром. Класи успадковують
CBitrixComponent або викликають CIBlockElement напряму. Будь-який тест вимагає ініціалізації всього оточення.
- Відсутність інтерфейсів. Репозиторії часто не виділені в окремі класи. Натомість SQL-запити розкидані по методах.
- Глобальні стани. Бітрікс використовує глобальні змінні (
$APPLICATION, $DB) та синглтони. Це ламає ізоляцію тестів.
- Повільний bootstrap. Завантаження ядра через
prolog_before.php займає 1–3 секунди, що робить прогін тисячі тестів неприйнятно довгим.
Через ці фактори багато розробників відмовляються від unit-тестування. Але ми знайшли підхід, який робить його ефективним.
Принцип тестованої архітектури
Перший крок — виділити бізнес-логіку в окремі класи з чіткими інтерфейсами. Не робіть так:
// Бізнес-логіка змішана з інфраструктурою — неможливо протестувати ізольовано
public function calculateDiscount(int $userId): float
{
$user = \CUser::GetByID($userId)->Fetch(); // статичний виклик Бітрікс
$orders = \CSaleOrder::GetList([], ['USER_ID' => $userId])->Fetch();
return $orders['count'] > 10 ? 0.15 : 0.05;
}
Натомість інжектуйте залежності:
// Логіка відокремлена, залежності інжектуються
class DiscountCalculator
{
public function __construct(
private UserRepositoryInterface $users,
private OrderRepositoryInterface $orders,
) {}
public function calculate(int $userId): float
{
$user = $this->users->findById($userId);
$orderCount = $this->orders->countByUserId($userId);
return $orderCount > 10 ? 0.15 : 0.05;
}
}
Репозиторії реалізуються через Бітрікс API в продакшн-коді та через моки в тестах. Такий рефакторинг окупається вже після першого циклу змін. Детальніше про впровадження залежностей у документації Бітрікс.
Інфраструктура тестів
Налаштовуємо PHPUnit з двома bootstraps: один для чистих unit-тестів (без ядра, лише Composer autoload), другий — для інтеграційних, що завантажують Бітрікс.
Приклад bootstrap для інтеграційних тестів:
// tests/bootstrap.php
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_WITH_ON_AFTER_EPILOG', false);
define('BX_NO_ACCELERATOR_RESET', true);
$_SERVER['DOCUMENT_ROOT'] = realpath(__DIR__ . '/../../../..');
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
\Bitrix\Main\Loader::includeModule('your.module');
Для ізольованих тестів — окремий bootstrap без prolog_before.php. Це прискорює прогін у 10–50 разів.
Приклади unit-тестів
Тест бізнес-логіки (без ядра):
class DiscountCalculatorTest extends TestCase
{
private DiscountCalculator $calculator;
protected function setUp(): void
{
$this->calculator = new DiscountCalculator(
users: $this->createStub(UserRepositoryInterface::class),
orders: $this->createConfiguredMock(
OrderRepositoryInterface::class,
['countByUserId' => 5]
),
);
}
public function testLessThan10OrdersGivesBasicDiscount(): void
{
$this->assertSame(0.05, $this->calculator->calculate(1));
}
public function testMoreThan10OrdersGivesPremiumDiscount(): void
{
$repo = $this->createConfiguredMock(
OrderRepositoryInterface::class,
['countByUserId' => 15]
);
$calc = new DiscountCalculator($this->createStub(UserRepositoryInterface::class), $repo);
$this->assertSame(0.15, $calc->calculate(1));
}
}
Інтеграційні тести з ядром використовуємо лише для перевірки ORM або складних ланцюжків викликів. Вони запускаються окремо, не в основному наборі.
Який ROI від unit-тестів?
Впровадження unit-тестів скорочує час на налагодження на 70% та знижує вартість підтримки вдвічі завдяки ранньому виявленню регресій. У середньому на одну бізнес-операцію (розрахунок, валідація) йде від 2 до 6 годин написання тестів. Повний цикл для модуля середнього розміру — 2–5 днів. Терміни залежать від поточної архітектури та необхідності рефакторингу. Якщо ви хочете підвищити стабільність проєкту, зв'яжіться з нами для аудиту тестованості.
Порівняння підходів
| Критерій |
Ізольовані unit-тести |
Інтеграційні з ядром |
| Швидкість виконання |
0.01–0.1 сек |
1–5 сек |
| Залежність від БД |
Ні |
Так |
| Вимагають моків |
Так |
Ні |
| Покривають бізнес-логіку |
Так |
Так |
| Покривають інфраструктуру |
Ні |
Так |
| Простота налагодження |
Висока |
Середня |
Ізольовані тести в 50 разів швидші — для більшості сценаріїв обираємо їх.
Покриття та пріоритизація
Не потрібно покривати тестами 100% коду. Пріоритети:
| Пріоритет |
Що тестуємо |
| Високий |
Розрахунки цін, знижок, вартості доставки |
| Високий |
Бізнес-логіка станів (статусна машина) |
| Високий |
Парсери та маппінг даних (імпорт з 1С, Excel) |
| Середній |
Валідатори вхідних даних |
| Середній |
Алгоритми формування звітів |
| Низький |
Шаблони компонентів, UI-логіка |
Цільове покриття для ключової бізнес-логіки — 80%+. Для інфраструктурного коду (репозиторії, адаптери) достатньо інтеграційних тестів. Це дає баланс між швидкістю та надійністю.
Що входить у написання unit-тестів
- Аудит коду модуля на тестованість, рефакторинг точок впровадження залежностей.
- Налаштування PHPUnit з bootstrap для Бітрікс-оточення.
- Написання тестів для бізнес-логіки: розрахунки, статусні машини, парсери.
- Налаштування звіту покриття через Xdebug.
- Інтеграція запуску тестів у CI (GitHub Actions / GitLab CI).
- Документація із запуску тестів та додавання нових.
Ми гарантуємо якість: досвід наших інженерів — понад 10 років у Бітрікс-розробці. Замовте написання unit-тестів під ключ — отримайте стабільний і легкопідтримуваний код. Оцінимо ваш проєкт безкоштовно, просто зв'яжіться з нами.
Типові помилки при написанні тестів для Бітрікс
- Спроба завантажувати ядро для кожного тесту: використовуйте два bootstrap'и.
- Використання реальної бази даних в unit-тестах: мокайте репозиторії.
- Ігнорування кешування результатів: скидайте кеш між тестами.
- Тестування захищених методів через рефлексію: виділіть логіку в публічні методи.
Тестування сайтів на 1С-Бітрікс
CIBlockElement::GetList і пагінація — баг, який живе роками
Класичний кейс: на сайті каталог із пагінацією через компонент bitrix:catalog.section. Замовник скаржиться — на третій сторінці дублюються товари. Лізеш у кеш компонента, чистиш — начебто ок. Через день знову. Виявляється, кастомне сортування конфліктує з параметром PAGEN_1, і при певній комбінації фільтрів CIBlockElement::GetList повертає ті самі ID. Такі штуки ловляться тільки тестуванням — не код-рев'ю, не «подивився очима».
Зламана пагінація, некоректний розрахунок знижок, помилки інтеграції з 1С — кожен з цих багів коштує бізнесу реальних грошей. Ми знаємо, як їх знайти і виправити до того, як вони вплинуть на продажі. Наша команда має понад 10 років досвіду з Бітрікс і понад 50 успішних QA-проєктів. Ми вибудовуємо QA-процес під проєкти на 1С-Бітрікс: ручне функціональне, автоматизовані E2E, навантажувальне та приймальне тестування.
Чому сайти на 1С-Бітрікс потребують професійного тестування?
Проєкти на 1С-Бітрікс — не лендінги. Під капотом — десятки модулів, інтеграції та неочевидні залежності:
- Ланцюжки в бізнес-логіці — виправив розрахунок знижок у
sale.discount, а промокод через sale.basket.discount перестав застосовуватися. Модуль знижок у Бітріксі — один із найкрихкіших: правила пріоритетів, перетини, накопичувальні програми. Одна правка — каскад збоїв.
- Інтеграція з 1С — обмін через
catalog.import.1c або REST. Збій у маппінгу властивостей інфоблоку — і на сайті товар без ціни або з нульовим залишком. Розсинхронізація замовлень — втрачені продажі.
- Оновлення ядра —
bitrix:main оновився, а кастомний компонент використовував deprecated-метод CModule::IncludeModule з нестандартними параметрами. Без регресії — російська рулетка.
- Мультибраузерність —
bitrix:sale.order.ajax рендерить форми по-різному в Safari і Chrome. Кнопка «Оформити замовлення» на iPhone може з'їхати за межі екрана.
Як функціональне тестування вирішує типові проблеми?
Перевіряємо кожен бізнес-сценарій. Не «працює-не працює», а всі граничні випадки.
Каталог (компоненти catalog.section, catalog.element)
- Розумний фільтр
catalog.smart.filter: усі комбінації властивостей, скидання, підрахунок результатів. Особливо — фільтри за торговими пропозиціями (SKU), вони ламаються найчастіше.
- Сортування + пагінація — той самий баг із дублями.
- Порівняння через
catalog.compare.list — додавання, видалення, відображення відмінностей.
- Швидкий перегляд — модальне вікно, кошик із модалки.
Кошик і замовлення (sale.basket.basket, sale.order.ajax)
- Додавання з каталогу, з картки, швидке замовлення.
- Знижки: за кількістю, за сумою, за купоном, за накопичувальною. Перетин знижок — окремий тест-кейс, мінімум 8 комбінацій.
- Розрахунок доставки: обробники
sale.delivery.services, вартість, терміни, ПВЗ на карті.
- Оплата:
sale.paysystem — проходження платежу, обробка відхилень, повернення.
- Формування замовлення: email через
main.mail.event, запис у CRM, передача в 1С через sale.export.1c.
Особистий кабінет (sale.personal.section)
- Реєстрація, авторизація, відновлення пароля — включно з edge-case із кириличним email.
- Історія замовлень, повторне замовлення.
- Підписки, бонусна програма.
Форми та пошук
-
form.result.new / iblock.element.add.form — відправка, валідація, файлові поля.
-
search.page — релевантність, морфологія, обробка помилок через search.title.
Регресійне тестування: як ми запобігаємо помилкам після оновлень
Після кожного деплою ми перевіряємо, чи не зламали те, що працювало.
- Smoke-тести — головна відкривається, каталог віддає товари, замовлення проходить до кінця. 5 хвилин, запускаємо після кожного деплою. Якщо smoke впав — відкочуємо, не розбираючись.
- Регресійний набір — 40–80 тест-кейсів за основними сценаріями. Перед кожним релізом.
- Візуальне тестування — порівняння скріншотів через Percy або Playwright. Кнопка з'їхала на 20px, шрифт змінився після оновлення — тест покаже diff.
- Чек-листи за модулями — структуровані списки для
sale, catalog, iblock, search. Кожен модуль — свій чек-лист.
Типовий набір регресійних тест-кейсів (скорочено)
- Головна сторінка: коректне відображення слайдера, категорій, блоку акцій.
- Каталог: фільтр без результатів, фільтр із однією властивістю, пагінація після зміни сортування.
- Картка товару: зміна кількості, різні торгові пропозиції, додавання в обране.
- Кошик: зміна кількості, видалення, застосування купона.
- Оформлення: успішна оплата, помилка оплати, повернення з помилкою.
Як визначити вузькі місця продуктивності магазину на Бітрікс?
Ми моделюємо навантаження, щоб зрозуміти, при якому RPS catalog.section почне віддавати 500-ку. Використовуємо реалістичні профілі на основі вашої аналітики.
Профіль навантаження для магазину на Бітрікс:
| Сценарій |
Частка |
Цільовий відгук |
Що ламається першим |
| Головна |
20% |
< 1 сек |
Композитний кеш, якщо не налаштований |
| Каталог із фільтрами |
30% |
< 2 сек |
MySQL — важкі JOIN по b_iblock_element_property |
| Картка товару |
25% |
< 1.5 сек |
Запити до торгових пропозицій |
| Додавання в кошик |
10% |
< 1 сек |
Блокування таблиці b_sale_basket |
| Оформлення замовлення |
5% |
< 3 сек |
Обробники доставки (зовнішні API) |
| Пошук |
10% |
< 2 сек |
b_search_content без індексів |
Інструменти: k6 (JavaScript-сценарії для кошика та чекауту), Apache JMeter (складні сценарії з cookie-авторизацією), Yandex.Tank (візуалізація в реальному часі). На виході — максимальний RPS, час відгуку за перцентилями p50/p95/p99, вузькі місця (CPU, RAM, MySQL slow queries на b_iblock_element, файловий кеш). Конкретні рекомендації: який індекс додати, який запит переписати на D7 ORM, де ввімкнути композитний кеш.
У проєкті з 100 000 товарами додавання індекса на b_iblock_element_property.IBLOCK_ELEMENT_ID скоротило час виконання фільтра з 8 секунд до 0.3 секунди — ми знайшли це саме під час навантажувального тестування.
Автоматизоване, кросбраузерне та приймальне тестування
Кросбраузерність перевіряємо там, де реально сидять покупці. Статистика з Метрики конкретного проєкту важливіша за загальноринкові дані. Мінімальний набір: Chrome (останні 2 версії), Safari на iOS, Яндекс.Браузер, Samsung Internet. Пристрої: Desktop 1920×1080 та 1366×768, iPhone 375×812 та 390×844 (обов'язково чекаут), Android 360×800 та 412×915. Інструменти: BrowserStack для реальних пристроїв, Playwright для автоматизації в Chromium/Firefox/WebKit.
Автоматизація базується на Playwright — кросбраузерність, паралельний запуск, автоматичні очікування, добре працює з динамічними формами sale.order.ajax. У порівнянні з ручним тестуванням, автоматизація скорочує час виконання регресії втричі. PHPUnit використовуємо для модульних тестів кастомних компонентів і бізнес-логіки — інтеграція з CI/CD (GitLab CI, GitHub Actions). Тестовий набір із 50 сценаріїв виконується за 15 хвилин замість годин ручної перевірки.
Фінальна перевірка (UAT) проводиться із замовником на staging з копією продової бази. Спільно складаємо 15–20 ключових шляхів покупця, фіксуємо баги в Jira/YouTrack, оформляємо протокол приймання.
QA-процес: як ми вбудовуємо тестування в розробку
Тестування не приклеюється наприкінці — воно стартує з аналізу вимог.
- Аналіз вимог — QA бере участь в обговоренні завдань, ловить неоднозначності. «Знижка застосовується до товару чи до замовлення?» — таке питання на старті економить два дні відладки.
- Тест-кейси до розробки — сценарії готові до першого рядка коду.
- Code review — перевірка на типові помилки Бітрікса: неочищений кеш компонентів, прямі SQL-запити замість ORM, відсутність перевірки
$USER->IsAuthorized().
- Функціональне → регресійне → деплой.
- Моніторинг після релізу — помилки в
bitrix/error.log, метрики в аналітиці, алерти по 500-м.
Закажіть тестування сьогодні — і ми підготуємо тест-план за 2 дні.
Терміни та вартість тестування
| Завдання |
Терміни |
| Тест-план |
2–3 дні |
| Функціональне тестування (середній магазин) |
3–5 днів |
| Базовий набір E2E-автотестів (Playwright) |
2–3 тижні |
| Навантажувальне тестування + звіт |
1–2 тижні |
| Кросбраузерне |
2–3 дні |
| UAT-супровід |
3–5 днів |
| QA-процес з нуля |
3–4 тижні |
Що входить у роботу: детальний тест-план із чек-листами за модулями, автоматизований набір E2E-тестів (Playwright/Cypress) під ваш стек, звіт із результатами, скріншотами помилок і рекомендаціями, інтеграція тестів у ваш CI/CD (GitLab CI, GitHub Actions, Bitbucket Pipelines), супровід UAT — до трьох ітерацій виправлень без додаткової оплати, документація процесу тестування для вашої команди.
Баг на продакшені — це не тільки вартість виправлення. Середнє виправлення критичного дефекту коштує від 20 000 грн, а втрати від зламаного кошика за вихідні на проєкті з оборотом 5 млн/міс можуть сягати 400 000 грн. Інвестиція в тестування — це страховка вашого бізнесу.
Зв'яжіться з нами, щоб отримати комерційну пропозицію та детальний план тестування для вашого проєкту.