Настройка PHPUnit-тестов для модулей 1С-Битрикс

Наша компания занимается разработкой, поддержкой и обслуживанием решений на Битрикс и Битрикс24 любой сложности. От простых одностраничных сайтов до сложных интернет магазинов, CRM систем с интеграцией 1С и телефонии. Опыт разработчиков подтвержден сертификатами от вендора.
Предлагаемые услуги
Показано 1 из 1 услугВсе 1626 услуг
Настройка PHPUnit-тестов для модулей 1С-Битрикс
Простая
~1 рабочий день
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Разработка на базе Битрикс, Битрикс24, 1С для компании Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    747
  • image_crm_dolbimby_434_0.webp
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    976

Настройка PHPUnit-тестов для модулей 1С-Битрикс

Модуль Битрикс без тестов — чёрный ящик. Исправили одно — сломали другое. Особенно болезненно это в модулях с бизнес-логикой: расчёт скидок, интеграции с внешними API, обработка заказов. PHPUnit для Битрикс-модулей имеет специфику: ядро Битрикс нужно загружать (и это медленно), статические вызовы мешают изоляции, а сам Битрикс предоставляет собственные инструменты тестирования для ORM.

Настройка PHPUnit-тестов для модулей 1С-Битрикс

Структура тестов в модуле Битрикс

/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

phpunit.xml для модуля Битрикс

<?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>

bootstrap.php: два режима

<?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-тест с изолированной бизнес-логикой

// 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% — максимум
    }
}

Integration-тест с ORM Битрикс

// tests/Integration/Repository/OrderRepositoryTest.php
namespace Tests\Integration\Repository;

use PHPUnit\Framework\TestCase;
use Vendor\Mymodule\Repository\OrderRepository;

class OrderRepositoryTest extends TestCase
{
    private static int $testUserId;

    public static function setUpBeforeClass(): void
    {
        // Создаём тестового пользователя
        $user = new \CUser();
        self::$testUserId = $user->Add([
            'LOGIN'      => 'test_' . uniqid(),
            'PASSWORD'   => 'Test123!',
            'EMAIL'      => 'test_' . uniqid() . '@test.ru',
            'ACTIVE'     => 'Y',
            'GROUP_ID'   => [2],
        ]);
    }

    public static function tearDownAfterClass(): void
    {
        // Удаляем тестовые данные
        \CUser::Delete(self::$testUserId);
    }

    public function testCountCompletedOrdersReturnsCorrectNumber(): void
    {
        $repo = new OrderRepository();

        // Создаём тестовые заказы через Sale API
        $this->createTestOrder(self::$testUserId, 'F'); // завершённый
        $this->createTestOrder(self::$testUserId, 'F');
        $this->createTestOrder(self::$testUserId, 'N'); // новый, не считаем

        $count = $repo->countCompletedByUser(self::$testUserId);
        $this->assertSame(2, $count);
    }

    private function createTestOrder(int $userId, string $status): void
    {
        \Bitrix\Main\Loader::includeModule('sale');

        $order = \Bitrix\Sale\Order::create('s1', $userId);
        $order->setField('STATUS_ID', $status);
        $order->setField('CURRENCY', 'RUB');
        $order->setField('PRICE', 1000.0);
        $order->save();
    }
}

Запуск только unit-тестов (без загрузки Битрикс)

# Быстрые 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

CI/CD конфигурация

# .github/workflows/tests.yml
- name: Run PHPUnit Unit tests
  run: |
    cd local/modules/vendor.mymodule
    PHPUNIT_NO_BITRIX=true vendor/bin/phpunit --testsuite Unit
  env:
    PHPUNIT_NO_BITRIX: 'true'

Сроки

Задача Сроки
Настройка PHPUnit, bootstrap, конфигурация для модуля 4–8 часов
Unit-тесты для бизнес-логики модуля (≤10 классов) 1–2 дня
Integration-тесты с ORM Битрикс 1–2 дня
Рефакторинг модуля для тестируемости + покрытие 70%+ 3–7 дней