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 |







