We know: a Bitrix module without tests is a black box. You fix one thing and break another. This is especially painful in modules with business logic: discount calculations, external API integrations, order processing. According to our statistics, 60% of regressions can be prevented by automated tests. Manual testing after each change takes 2-3 hours, automated testing takes 2-3 minutes. One missed side effect — and on a live site, prices drop or notifications fail. PHPUnit tests catch such errors in seconds and run 10 times faster than manual checks.
We integrate PHPUnit into Bitrix modules for dozens of projects. Setting up testing for Bitrix modules has its own specifics: the kernel must be loaded, static calls interfere with isolation. We offer a turnkey solution — PHPUnit setup tailored to your module's architecture. Our clients have reduced regression testing time by 70% and the number of errors in releases by 80%.
Why PHPUnit tests in Bitrix?
Manual module testing after each change is slow and unreliable. One missed side effect — and on a live site, prices drop or notifications fail. Automated tests catch such errors in seconds. They run 10 times faster than manual checks and don't miss regressions.
How we do it: a real case
On one project, a discount calculation module worked correctly in rubles, but when switching currency to euros, the discount doubled due to a rounding error. Manual testing didn't reveal the issue because they only tested with rubles. After implementing PHPUnit tests for all currency scenarios, the error was caught in one minute. Now, with every change to the discount logic, a set of 30 tests runs that check edge cases.
Test structure in a Bitrix module
/local/modules/vendor.mymodule/
lib/
Services/
DiscountService.php
ShippingCalculator.php
Repository/
OrderRepository.php
tests/
bootstrap.php
Unit/
Services/
DiscountServiceTest.php
ShippingCalculatorTest.php
Integration/
Repository/
OrderRepositoryTest.php
phpunit.xml
composer.json
Environment setup: phpunit.xml and bootstrap
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="tests/bootstrap.php"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
requireCoverageMetadata="false"
beStrictAboutCoverageMetadata="false"
>
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
<testsuite name="Integration">
<directory>tests/Integration</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory suffix=".php">lib</directory>
</include>
</source>
<coverage>
<report>
<html outputDirectory="tests/_coverage"/>
<clover outputFile="tests/_coverage/clover.xml"/>
</report>
</coverage>
</phpunit>
<?php
// tests/bootstrap.php
$bitrixLoaded = false;
// Unit-тесты без ядра Битрикс — быстро
if (getenv('PHPUNIT_NO_BITRIX') === 'true') {
require_once __DIR__ . '/../vendor/autoload.php';
return;
}
// Integration-тесты с ядром Битрикс — медленнее
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_WITH_ON_AFTER_EPILOG', false);
define('BX_NO_ACCELERATOR_RESET', true);
define('STOP_STATISTICS', true);
$docRoot = realpath(__DIR__ . '/../../../..');
$_SERVER['DOCUMENT_ROOT'] = $docRoot;
$_SERVER['HTTP_HOST'] = 'localhost';
$_SERVER['SERVER_NAME'] = 'localhost';
require_once $docRoot . '/bitrix/modules/main/include/prolog_before.php';
require_once __DIR__ . '/../vendor/autoload.php';
\Bitrix\Main\Loader::includeModule('vendor.mymodule');
Unit test example with isolated business logic
// tests/Unit/Services/DiscountServiceTest.php
namespace Tests\Unit\Services;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Vendor\Mymodule\Services\DiscountService;
use Vendor\Mymodule\Repository\OrderRepositoryInterface;
use Vendor\Mymodule\Repository\UserRepositoryInterface;
class DiscountServiceTest extends TestCase
{
private DiscountService $service;
private OrderRepositoryInterface&MockObject $orders;
private UserRepositoryInterface&MockObject $users;
protected function setUp(): void
{
$this->orders = $this->createMock(OrderRepositoryInterface::class);
$this->users = $this->createMock(UserRepositoryInterface::class);
$this->service = new DiscountService($this->orders, $this->users);
}
public function testNewUserGetsNoDiscount(): void
{
$this->orders->method('countCompletedByUser')->willReturn(0);
$this->users->method('getRegistrationDays')->willReturn(5);
$discount = $this->service->calculate(userId: 1, orderAmount: 5000.0);
$this->assertSame(0.0, $discount);
}
public function testUserWith5OrdersGets5PercentDiscount(): void
{
$this->orders->method('countCompletedByUser')->willReturn(5);
$this->users->method('getRegistrationDays')->willReturn(180);
$discount = $this->service->calculate(userId: 1, orderAmount: 5000.0);
$this->assertSame(250.0, $discount); // 5% от 5000
}
public function testDiscountCappedAt20Percent(): void
{
$this->orders->method('countCompletedByUser')->willReturn(100);
$this->users->method('getRegistrationDays')->willReturn(1000);
$discount = $this->service->calculate(userId: 1, orderAmount: 10000.0);
$this->assertSame(2000.0, $discount); // 20% — максимум
}
}
How to speed up and automate tests?
The main technique is separating tests into unit and integration. Unit tests run without the Bitrix kernel in seconds, integration tests with the kernel in minutes. In our bootstrap, the PHPUNIT_NO_BITRIX environment variable is used: if set, tests run without loading the kernel. This allows unit tests to be run on every code change, while integration tests run less frequently, e.g., when merging to the main branch.
# Быстрые unit-тесты без ядра Битрикс (секунды)
PHPUNIT_NO_BITRIX=true vendor/bin/phpunit --testsuite Unit
# Интеграционные тесты с ядром (минуты)
vendor/bin/phpunit --testsuite Integration
# Все тесты с покрытием (требует Xdebug или PCOV)
XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html tests/_coverage
For CI/CD, we configure runs in GitHub Actions, GitLab CI, or Jenkins. Unit tests execute on every push, integration tests before merging. This provides feedback in 2 minutes. If tests pass, code is automatically deployed to staging. Coverage of key paths reaches 95%, minimizing regression risk.
What's included in the turnkey testing setup
| Deliverable | Description |
|---|---|
| Module audit | Identify critical areas for testing, assess current architecture |
| PHPUnit setup | Bootstrap, phpunit.xml, environment for unit and integration tests |
| Test writing | Cover key business logic: discounts, cart, integrations |
| CI/CD integration | Add test runs to GitHub Actions, GitLab CI, or Jenkins |
| Documentation | Test descriptions, run and maintenance instructions |
| Team training | How to write new tests and maintain coverage |
Estimated timelines
| Task | Timeline |
|---|---|
| PHPUnit setup, bootstrap, configuration for the module | 4–8 hours |
| Unit tests for module business logic (≤10 classes) | 1–2 days |
| Integration tests with Bitrix ORM | 1–2 days |
| Module refactoring for testability + 70%+ coverage | 3–7 days |
Our approach and guarantees
Certified Bitrix specialists. Over 10 years of development experience and 30+ projects with testing implementation. We guarantee that tests will run in your environment and produce stable results. Contact us to discuss setting up tests for your module. Get a consultation — we'll assess your module and propose an implementation plan.
Our approach to solving problems
Each task requires individual analysis and careful planning. We do not use template solutions—each project is adapted to specific requirements and existing infrastructure. Our team has experience with projects of various scales: from small stores to high-load platforms with millions of operations per day.
Guarantees and support
We provide a 12-month warranty on the work performed. During this period, we fix any issues that arise for free. After the project is completed, we provide full documentation and training for your team. Technical support is available for 30 days after launch—we'll help resolve any questions.







