Setting up Codeception tests for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Setting Up Codeception Tests for 1C-Bitrix

Codeception is a PHP testing framework that combines unit, functional, and acceptance (E2E) tests in a single tool. For 1C-Bitrix this is especially convenient: you can write functional tests that test PHP logic without a browser, and acceptance tests via WebDriver — all in one stack, with shared helpers and a single config.

Setting Up Codeception Tests for 1C-Bitrix

Installation and Structure

# Install in /local/ or in the project root
composer require --dev codeception/codeception
composer require --dev codeception/module-webdriver
composer require --dev codeception/module-phpbrowser
composer require --dev codeception/module-db

# Initialize
vendor/bin/codecept bootstrap

Structure after initialization:

tests/
    Acceptance/
        CatalogCest.php
        CheckoutCest.php
        AuthCest.php
    Functional/
        OrderCest.php
        CatalogFilterCest.php
    Unit/
        PriceCalculatorTest.php
        DeliveryZoneTest.php
    _support/
        AcceptanceTester.php
        FunctionalTester.php
        Helper/
            Bitrix.php    <- helper for Bitrix
    _data/
        dump.sql
codeception.yml

codeception.yml for a 1C-Bitrix Project

namespace: Tests
support_namespace: Support

paths:
    tests: tests
    output: tests/_output
    data: tests/_data
    support: tests/_support
    envs: tests/_envs

settings:
    shuffle: false
    lint: true

params:
    - .env.test

extensions:
    enabled:
        - Codeception\Extension\RunFailed

suites:
    acceptance:
        actor: AcceptanceTester
        modules:
            enabled:
                - WebDriver:
                    url: '%SITE_URL%'
                    browser: chrome
                    window_size: 1280x900
                    capabilities:
                        goog:chromeOptions:
                            args: ['--headless', '--no-sandbox']
                    host: selenium
                    port: 4444
                - Db:
                    dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%'
                    user: '%DB_USER%'
                    password: '%DB_PASS%'

    functional:
        actor: FunctionalTester
        modules:
            enabled:
                - Bitrix:
                    document_root: '%DOCUMENT_ROOT%'
                - Db:
                    dsn: 'mysql:host=%DB_HOST%;dbname=%DB_NAME%'
                    user: '%DB_USER%'
                    password: '%DB_PASS%'

    unit:
        actor: UnitTester

Helper for the Bitrix Environment

// tests/_support/Helper/Bitrix.php
namespace Tests\Support\Helper;

use Codeception\Module;

class Bitrix extends Module
{
    public function _beforeSuite(array $settings = []): void
    {
        $docRoot = $this->config['document_root'];

        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'] = $docRoot;

        if (!defined('B_PROLOG_INCLUDED')) {
            require_once $docRoot . '/bitrix/modules/main/include/prolog_before.php';
        }
    }

    public function loginAs(string $login, string $password): bool
    {
        global $USER;
        $USER = new \CUser();
        return (bool)$USER->Login($login, $password);
    }

    public function addProductToCart(int $productId, int $quantity = 1): int
    {
        \Bitrix\Main\Loader::includeModule('sale');
        \Bitrix\Main\Loader::includeModule('catalog');

        $basket = \Bitrix\Sale\Basket::loadItemsForFUser(
            \CSaleBasket::GetBasketUserID(),
            \Bitrix\Main\Context::getCurrent()->getSite()
        );

        $item = $basket->createItem('catalog', $productId);
        $item->setField('QUANTITY', $quantity);
        $basket->save();

        return $basket->getOrderableItems()->count();
    }
}

Functional Test: Order via the Bitrix API

// tests/Functional/OrderCest.php
namespace Tests\Functional;

use Tests\Support\FunctionalTester;

class OrderCest
{
    public function _before(FunctionalTester $I): void
    {
        // Clear the cart before each test
        $I->executeQuery('DELETE FROM b_sale_basket WHERE FUSER_ID = ?', [1]);
    }

    public function addItemAndCreateOrder(FunctionalTester $I): void
    {
        // Add product to cart via helper
        $I->loginAs('testuser', 'testpassword');
        $count = $I->addProductToCart(42, 2); // product ID=42, quantity 2
        $I->assertEquals(1, $count, 'Cart should contain 1 product');

        // Create order via Sale API
        $I->executeCustomAction(function () {
            \Bitrix\Main\Loader::includeModule('sale');

            $basket  = \Bitrix\Sale\Basket::loadItemsForFUser(1, 's1');
            $order   = \Bitrix\Sale\Order::create('s1', 1); // site, user
            $order->setBasket($basket);
            $order->setField('CURRENCY', 'RUB');

            $propertyCollection = $order->getPropertyCollection();
            $prop = $propertyCollection->getPayerName();
            $prop?->setValue('Test User');

            $result = $order->save();
            return $result;
        }, function ($result) use ($I) {
            $I->assertTrue($result->isSuccess(), implode(', ', $result->getErrorMessages()));
        });

        // Verify the order was created in the database
        $I->seeInDatabase('b_sale_order', [
            'USER_ID'  => 1,
            'CURRENCY' => 'RUB',
            'STATUS_ID' => 'N',
        ]);
    }
}

Acceptance Test via WebDriver

// tests/Acceptance/CatalogCest.php
namespace Tests\Acceptance;

use Tests\Support\AcceptanceTester;

class CatalogCest
{
    public function filterByBrandShowsCorrectProducts(AcceptanceTester $I): void
    {
        $I->amOnPage('/catalog/tools/');
        $I->waitForElement('.catalog-filter', 10);

        // Apply brand filter
        $I->checkOption('[data-filter="brand"][value="bosch"]');
        $I->waitForElementNotVisible('.catalog-loading', 10);

        // Verify URL
        $I->seeInCurrentUrl('brand=bosch');

        // Verify all cards are Bosch
        $I->seeNumberOfElementsGreaterThan('.product-card', 0);
        $brands = $I->grabMultiple('.product-brand');
        foreach ($brands as $brand) {
            $I->assertEquals('Bosch', $brand);
        }
    }
}

Running Tests

# All tests
vendor/bin/codecept run

# Acceptance only
vendor/bin/codecept run acceptance

# Specific test
vendor/bin/codecept run acceptance CatalogCest:filterByBrandShowsCorrectProducts

# With verbose output
vendor/bin/codecept run --steps --debug

Timelines

Task Timeline
Set up Codeception + Bitrix helper + suite configuration 1 day
Functional tests for business logic (orders, cart, calculations) 1–3 days
Acceptance tests via WebDriver (catalog, checkout, auth) 2–4 days