Инженерный контур 1С. Часть 5 - Конвейер непрерывной интеграции (CI/CD), эфемерные тестовые среды и автоматизированные барьеры приемки кода | infolimp.ru

Инженерный контур 1С. Часть 5 - Конвейер непрерывной интеграции (CI/CD), эфемерные тестовые среды и автоматизированные барьеры приемки кода

5 сентября 2026 · infolimp.ru

Построение конвейера непрерывной интеграции (CI/CD) в среде 1С на базе изолированных эфемерных контейнеров Docker. Конфигурирование автоматизированных рубежей приемки кода (Quality Gates): статический анализ исходных текстов BSL, выполнение сценариев модульного тестирования на базе фреймворка YAxUnit, генерация отчетов в формате JUnit и обеспечение цикла обратной связи (Feedback Loop) менее 10 минут.

Об иллюстративном кейсе. Сквозной пример «Торговый контур» в этой статье - обобщённый собирательный сценарий, а не описание конкретной компании или проекта. Цифры и симптомы типичны для нагруженных инсталляций 1С:ERP/КА с СУБД PostgreSQL на Linux и приведены для наглядности инженерных решений, а не как отчёт о конкретном внедрении.

Контекст и аудитория


В чём проблема

1. Несостоятельность ручного тестирования на масштабе Enterprise

При развитии сложных прикладных решений класса 1C:ERP объем функциональных связей между подсистемами растет нелинейно. Если конфигурация содержит $M$ подсистем и $N$ ключевых транзакционных документов, то число потенциальных межмодульных взаимодействий пропорционально комбинаторному числу связей:

$$K_{\text{interact}} = \mathcal{O}(M^2 + N \cdot M)$$

При внесении точечного изменения инженером подсистемы маркировки в модуль проведения документа РеализацияТоваровУслуг ручной контроль неизбежно сталкивается с фундаментальными барьерами:

Ручной контроль:
Разработчик --(Push ветки)--> Pull Request --(Визуальный Code Review)--> Merge
                                                    |
                   [Человеческий фактор: пропуск антипаттерна]
                                                    |
                                                    ▼
Промышленная среда: Запрос в цикле --> Блокировка СУБД --> Инцидент (Change Failure)
  1. Экспоненциальный рост трудоемкости сквозного регрессионного тестирования: Проверка всех смежных цепочек (взаиморасчеты, движения партий, списание остатков, формирование проводок, регистрация кодов маркировки) вручную занимает десятки человеко-часов на каждый релиз. В результате регрессионное тестирование либо урезается до поверхностных проверок, либо становится «бутылочным горлышком», затягивающим время доставки изменений (Lead Time).
  2. Пропуск архитектурных антипаттернов на этапе визуального рецензирования (Code Review): Даже опытный архитектор при инспекции объемного диффа (diff) в 1 500 строк кода не способен визуально выявить:
  3. Неявные запросы внутри циклов выборки табличных частей;
  4. Вызовы методов, неявно открывающих транзакции базы данных до проверки условий валидности данных;
  5. Забытые неиспользуемые переменные, удерживающие ссылки на тяжелые объекты в оперативной памяти;
  6. Превышение порога когнитивной сложности алгоритмов проведения.
  7. Высокая стоимость ошибки в продуктивном контуре: Обнаружение ошибки блокировки или расхождения движений в промышленной эксплуатации требует экстренного отката релиза, восстановления целостности транзакций и повторного цикла развертывания.

2. Деградация постоянных тестовых сред (Persistent Test Environments)

Традиционный подход к организации тестирования в 1С предполагает развертывание постоянной тестовой базы (копии рабочей базы или урезанного стенда):

Постоянная база:
Тест 1 (создал товар X) --> Тест 2 (рассчитывает на нулевой остаток X) --> Ошибка!
         |                                                                   ▲
         +------- Накопление мусорных данных / поломка нумераторов ----------+

Эксплуатация постоянных сред сопряжена с критическими недостатками: - Замусоривание и взаимное влияние тестов (Test Cross-Contamination): Если тестовый сценарий создает элементы справочников или формирует нераспределенные остатки, последующие тесты, рассчитывающие на исходное состояние таблиц, падают с ложноположительными ошибками. - Деградация структуры и рассинхронизация схем метаданных: При тестировании параллельных функциональных веток регулярное обновление конфигурации базы данных приводит к коллизиям типов и зависанию блокировок сеансов. - Высокие эксплуатационные расходы на обслуживание: Необходимость регулярного скриптового восстановления базы из продуктивного бэкапа с последующим обезличиванием персональных данных (Data Masking) требует терабайтов дискового пространства и часов процессорного времени.

3. Теоретическое обоснование концепции эфемерных тестовых сред

Решением указанных проблем выступает переход к эфемерным средам тестирования (Ephemeral Test Environments):

$$\text{Свойство эфемерности}: \quad \forall \text{ Pipeline} \implies \text{Init}(\text{Env}_0) \longrightarrow \text{Execute}(\text{Tests}) \longrightarrow \text{Destroy}(\text{Env}_0)$$

Эфемерная среда создается динамически в момент запуска конвейера непрерывной интеграции из эталонного состояния исходного кода Git, исполняет предписанный набор проверок и безусловно уничтожается после завершения задания.

Ключевые преимущества эфемерных сред: - 100% изоляция и воспроизводимость: Любой сбой воспроизводится детерминированно независимо от времени суток и действий других инженеров; - Нулевая стоимость хранения между запусками: Вычислительные ресурсы СУБД и дисковое пространство утилизируются исключительно во время прогона тестов; - Возможность параллелизации конвейеров: Несколько десятков Pull Request могут тестироваться одновременно в независимых изолированных контейнерах.


Архитектура решения

1. Архитектурная диаграмма конвейера непрерывной интеграции

Конвейер непрерывной интеграции (CI/CD pipeline) для конфигурации «1C:ERP 2.5» организован по многоуровневому принципу с постепенным увеличением глубины и ресурсоемкости проверок:

2. Разделение уровней контроля качества

Уровень контроля Применяемый инструментарий Время реакции Проверяемые критерии
L1. Статический анализ (Linting) bsl-language-server, xmllint $30 - 45$ сек Соответствие стандартам разработки, отсутствие неиспользуемых переменных, запрет запросов в циклах, когнитивная сложность $\le 20$.
L2. Сборка метаданных CLI платформы ibcmd $2.5 - 3.5$ мин Синтаксическая корректность XML-описания метаданных, валидность внутренних связей UUID, успешное применение схемы к СУБД.
L3. Модульное тестирование YAxUnit, 1cv8c (Headless) $3 - 4$ мин Корректность бизнес-логики проведения документов, правильность расчета формул и налогов, верификация движений в регистрах.
L4. Quality Gate Парсер артефактов (Python/Shell) $5 - 10$ сек Агрегация метрик всех уровней, принятие жесткого булевого решения о допуске ветки к слиянию с основной веткой.

3. Математическая модель времени выполнения конвейера непрерывной интеграции

Общее время прохождения конвейера непрерывной интеграции $T_{\text{pipeline}}$ складывается из времени последовательных и параллельных фаз:

$$T_{\text{pipeline}} = T_{\text{lint}} + T_{\text{init_db}} + \sum_{i=1}^{N} \frac{T_{\text{test}i}}{P{\text{workers}}} + T_{\text{qg}}$$

Где: - $T_{\text{lint}}$ - время статического анализа кодовой базы анализатором BSL LS; - $T_{\text{init_db}}$ - время создания базы данных в PostgreSQL и загрузки метаданных утилитой ibcmd; - $N$ - общее количество зарегистрированных модульных тестов YAxUnit; - $T_{\text{test}i}$ - время исполнения $i$-го тестового сценария; - $P{\text{workers}}$ - коэффициент параллелизма потоков исполнения тестов; - $T_{\text{qg}}$ - время оценки метрик и принятия решения по Quality Gate.

Для обеспечения высокой продуктивности инженерной команды установлено жесткое граничное условие целевого времени обратной связи:

$$T_{\text{pipeline}} \le 10\text{ минут}$$

Для достижения данного норматива в рамках высоконагруженного «Торгового контура» 1C:ERP 2.5 архитектурно реализованы следующие оптимизации: 1. Размещение СУБД PostgreSQL в оперативной памяти (tmpfs): Исключение физических операций дискового ввода-вывода (I/O) ускоряет создание базы и запись таблиц метаданных в $4.5$ раза ($T_{\text{init_db}}$ снижается с 12 минут до 2.5 минут); 2. Отключение надежности транзакций СУБД для тестового контура: Использование параметров fsync=off, synchronous_commit=off, full_page_writes=off, допустимое исключительно в одноразовых эфемерных базах; 3. Транзакционная изоляция модульных тестов: Выполнение каждого теста внутри транзакции с последующим откатом (Rollback), исключающее необходимость пересоздания базы между тестами.


Пошаговая реализация

Шаг 1. Конфигурирование статического анализатора bsl-language-server

Статический анализ исходного кода обеспечивает мгновенное обнаружение дефектов без необходимости компиляции и запуска платформы 1С (принцип Fail-Fast).

В конфигурационном файле .bsl-language-server.json зафиксированы строгие правила контроля качества для «Торгового контура»:

{
  "$schema": "https://raw.githubusercontent.com/1c-syntax/bsl-language-server/master/src/main/resources/bsl-language-server.schema.json",
  "language": "ru",
  "configurationRoot": "src",
  "diagnostics": {
    "computeTrigger": "onSave",
    "parameters": {
      "CognitiveComplexity": {
        "maxComplexity": 20
      },
      "LineLength": {
        "maxLineLength": 140
      }
    },
    "rules": {
      "CognitiveComplexity": true,
      "UnusedLocalVariable": true,
      "UnusedMethodParameters": true,
      "QueryInLoop": true,
      "ImplicitTransaction": true,
      "BeginTransactionWithoutTry": true,
      "UsingModalWindows": true
    }
  },
  "reporters": [
    {
      "type": "generic-issue",
      "path": "build/reports/bsl-generic-issues.json"
    },
    {
      "type": "junit",
      "path": "build/reports/bsl-junit.xml"
    }
  ]
}

Разбор критических правил статического анализатора:

  1. CognitiveComplexity (порог $\le 20$): Вычисляет когнитивную сложность процедур и функций по методологии SonarSource. Ограничивает вложенность условий Если...ИначеЕсли, циклов и логических операторов. Предотвращает появление монолитных «спагетти-методов» проведения документов, не поддающихся модульному тестированию.
  2. QueryInLoop (Критическая ошибка): Категорически запрещает размещение конструкций создания и выполнения запросов Запрос.Выполнить() внутри циклических конструкций Для...Цикл и Пока...Цикл. Предотвращает лавинообразный рост нагрузки на СУБД (проблема $\mathcal{O}(N)$ обращений к базе вместо одного пакетного запроса).
  3. ImplicitTransaction (Критическая ошибка): Детектирует вызовы платформенных методов записи объектов метаданных (Записать(), Удалить()), порождающих неявные транзакции базы данных вне явного управления транзакционным контекстом. Исключает неконтролируемые эскалации блокировок.
  4. UnusedLocalVariable и UnusedMethodParameters: Блокирует накопление неиспользуемых переменных и параметров, сохраняя чистоту кодовой базы и снижая утечки памяти.

Шаг 2. Создание Docker-образа тестового раннера и СУБД в оперативной памяти (tmpfs)

Для исключения зависимости от физической инфраструктуры тестовый контур упакован в легковесные контейнеры.

1. Оптимизация СУБД PostgreSQL для эфемерных баз

В файле docker-compose.test.yml каталог данных PostgreSQL монтируется в виртуальный диск в оперативной памяти (tmpfs):

version: '3.8'

networks:
  ci-bridge:
    driver: bridge

services:
  postgres-test:
    image: postgres:16-alpine
    container_name: postgres-test-ephemeral
    restart: "no"
    networks:
      - ci-bridge
    environment:
      POSTGRES_DB: erp_test_db
      POSTGRES_USER: usr1cv8
      POSTGRES_PASSWORD: TestPassword1C
      PGDATA: /var/lib/postgresql/data/pgdata
    tmpfs:
      - /var/lib/postgresql/data:rw,noexec,nosuid,size=2048m
    command: >
      postgres
        -c shared_buffers=512MB
        -c work_mem=64MB
        -c synchronous_commit=off
        -c fsync=off
        -c full_page_writes=off
        -c checkpoint_timeout=30min
        -c max_wal_size=2GB
        -c listen_addresses='*'
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U usr1cv8 -d erp_test_db"]
      interval: 2s
      timeout: 3s
      retries: 15

За счет директив fsync=off и synchronous_commit=off СУБД не выполняет синхронизацию буферов с физическим накопителем, производя все транзакционные операции исключительно в оперативной памяти RAM со скоростью шины памяти (до 25–40 ГБ/с).

2. Сборка контейнера тестового раннера 1С

Файл Dockerfile.test-runner формирует автономный образ на базе Ubuntu 22.04 LTS: - Включает платформенную утилиту администрирования ibcmd и клиент 1cv8c; - Содержит OpenJDK 17 для исполнения анализатора bsl-language-server; - Содержит предустановленный релизный пакет фреймворка YAxUnit; - Настроен на строгую локаль ru_RU.UTF-8 для исключения проблем с русскоязычными идентификаторами метаданных.


Шаг 3. Разработка изолированных модульных тестов YAxUnit

Фреймворк YAxUnit представляет собой xUnit-совместимый инструмент тестирования для платформы 1С, поддерживающий запуск тестов без графического интерфейса пользователя и выгрузку отчетов в стандартном формате JUnit XML.

Конфигурация параметров запуска зафиксирована в файле yaxunit.json:

{
  "$schema": "https://raw.githubusercontent.com/bia-technologies/yaxunit/master/docs/schema/yaxunit-config.schema.json",
  "testsPath": "articles/05-ci-cd/artifacts/yaxunit/tests",
  "exitCode": true,
  "closeApp": true,
  "reporters": {
    "junit": {
      "enabled": true,
      "path": "build/reports/junit-report.xml"
    },
    "allure": {
      "enabled": true,
      "path": "build/reports/allure-results"
    }
  }
}

Архитектурный паттерн транзакционной изоляции тестов

Главный критерий надежности модульного теста - независимость и детерминированность. Тест не должен оставлять следов в базе данных, которые могут повлиять на последующие тесты.

В модуле Тест_ПроведениеДокументаРеализация.bsl реализован эталонный шаблон изолированного тестирования проведения документа:

// Тест: Верификация проведения реализации и контроля маркировки
Процедура Тест01_ПроведениеРеализации_ФормированиеДвиженийСкладаИРасчетНДС() Экспорт

    НачатьТранзакцию();

    Попытка
        // 1. Формирование тестового окружения метаданных в транзакции
        КонтекстТеста = ИнициализироватьТестовыйКонтекст();

        // 2. Создание и заполнение проверяемого документа
        ДокументОбъект = Документы.РеализацияТоваровУслуг.СоздатьДокумент();
        ДокументОбъект.Дата = ТекущаяДатаСессии();
        ДокументОбъект.Организация = КонтекстТеста.Организация;
        ДокументОбъект.Контрагент = КонтекстТеста.Контрагент;
        ДокументОбъект.Склад = КонтекстТеста.Склад;

        СтрокаТовары = ДокументОбъект.Товары.Добавить();
        СтрокаТовары.Номенклатура = КонтекстТеста.Номенклатура;
        СтрокаТовары.Количество = 10;
        СтрокаТовары.Цена = 1000;
        СтрокаТовары.Сумма = 10000;
        СтрокаТовары.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
        СтрокаТовары.СуммаНДС = 2000;
        СтрокаТовары.СуммаСНДС = 12000;

        // 3. Вызов проведения в Headless-режиме
        ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);

        // 4. Проверка утверждений (Assertions)
        ЮТест.ОжидаетЧто(ДокументОбъект.Проведен, "Флаг проведения документа").ЭтоИстина();
        ЮТест.ОжидаетЧто(ДокументОбъект.Товары.Итог("СуммаНДС"), "Итоговая сумма НДС").Равно(2000);

        // 5. Проверка движений в регистре накопления «ТоварыНаСкладах»
        ДвиженияСклада = ПолучитьДвиженияТоварыНаСкладах(ДокументОбъект.Ссылка);
        ЮТест.ОжидаетЧто(ДвиженияСклада.Количество(), "Количество движений").Равно(1);
        ЮТест.ОжидаетЧто(ДвиженияСклада[0].ВидДвижения, "Вид движения").Равно(ВидДвиженияНакопления.Расход);
        ЮТест.ОжидаетЧто(ДвиженияСклада[0].Количество, "Объем списания").Равно(10);

    Исключение
        Если ВТранзакции() Тогда
            ОтменитьТранзакцию();
        КонецЕсли;
        ВызватьИсключение;
    КонецПопытки;

    // Гарантированный откат транзакции: база остается кристально чистой
    Если ВТранзакции() Тогда
        ОтменитьТранзакцию();
    КонецЕсли;

КонецПроцедуры

Преимущества данного шаблона: 1. Абсолютная чистота среды: База данных после завершения теста не содержит ни созданного документа, ни записей движений, ни вспомогательных элементов справочников; 2. Высокая скорость исполнения: Откат транзакции в оперативной памяти занимает менее $10$ миллисекунд; 3. Отсутствие взаимных блокировок: Тесты могут исполняться в любом порядке.


Шаг 4. Настройка многоэтапного конвейера непрерывной интеграции

Для автоматизации полного цикла проверок настроены декларативные описания конвейера для двух распространенных enterprise-платформ:

1. Реализация для GitLab CI: .gitlab-ci.yml

Конвейер состоит из 4 последовательно-параллельных стадий:

stages:
  - lint          # Статический анализ исходного кода (Fail-Fast)
  - build         # Инициализация ИБ через ibcmd infobase create / config load
  - test          # Пакетный запуск YAxUnit в headless-режиме
  - quality-gate  # Анализ артефактов и принятие решения о блокировке MR

2. Реализация для GitHub Actions: github-actions-ci.yml

Оркестрирует аналогичный пайплайн в облачной или self-hosted инфраструктуре GitHub с визуализацией результатов в нативном интерфейсе pull request:

[GitHub PR] --► Job: lint (BSL LS Java 17) 
            --► Job: test (PostgreSQL Service in tmpfs -> ibcmd -> YAxUnit)
            --► Job: quality-gate (Evaluation of JUnit XML and Issue Reports)

Шаг 5. Формирование политики Quality Gate и правил блокировки слияния веток

Автоматизированный порог качества (Quality Gate) - это программный барьер, переводящий статус проверки в статус Failed, если хотя бы один из показателей качества не удовлетворяет регламенту.

Алгоритм принятия решения Quality Gate:

# Логика оценки порогов качества в конвейере
def evaluate_quality_gate(junit_report_path, issues_report_path):
    # 1. Проверка модульных автотестов
    failures, errors, total_tests = parse_junit(junit_report_path)
    if failures > 0 or errors > 0:
        raise QualityGateViolation(f"Зафиксировано {failures + errors} упавших модульных тестов!")
    if total_tests == 0:
        raise QualityGateViolation("Тестовый набор пуст! Слияние запрещено.")

    # 2. Проверка критических нарушений статического анализа
    critical_issues = parse_bsl_issues(issues_report_path, severities=["CRITICAL", "BLOCKER"])
    if len(critical_issues) > 0:
        raise QualityGateViolation(f"Обнаружено {len(critical_issues)} критических дефектов кода!")

    return "Quality Gate Passed Successfully"

В интерфейсе GitLab/GitHub настраивается правило защиты веток (Protected Branches): слияние функциональной ветки в ветку main или release заблокировано на уровне прав доступа до тех пор, пока задание quality-gate:evaluate не завершится с кодом 0.


Проверка результата и метрики

Внедрение конвейера непрерывной интеграции, контейнеризированного тестирования YAxUnit и Quality Gates в промышленном контуре «Торговый контур» (1C:ERP 2.5, 8 инженеров) позволило собрать точные метрики за 6 месяцев промышленной эксплуатации.

Сравнительные метрики надежности и скорости поставки

Показатель инженерного процесса Базовый уровень (Ручное тестирование) Целевой уровень (CI/CD + YAxUnit + Quality Gates) Результат модернизации
Доля дефектов, проникающих в промышленный контур (Change Failure Rate) 15.4% (1–2 критических инцидента на каждые 10 поставок) < 0.8% (менее 1 инцидента на 120 поставок) Снижение аварийности в 19.2 раза
Время получения обратной связи инженером (Feedback Loop Time) 24–48 часов (ожидание очереди ручного тестирования) 7.5 минут (автоматический прогон в контейнере) Ускорение обратной связи в 200+ раз
Охват модульными автотестами ключевых транзакционных модулей 0% (тесты отсутствовали) 64.2% (покрыты модули проведения документов реализации и заказов) Полная защита ядра бизнес-логики
Длительность регрессионного тестирования релиза 3–5 рабочих дней команды тестирования 25 минут (полный ночной запуск расширенного набора тестов) Сокращение релизного цикла на 95%
Субъективность на этапе Code Review Высокая (споры о стилях, пропуск скрытых дефектов) Нулевая (формализованные правила BSL LS и Quality Gate) 100% алгоритмический контроль стандартов
Динамика коэффициента Change Failure Rate (доля дефектных релизов):
Период:     Месяц 1     Месяц 2     Месяц 3     Месяц 4     Месяц 5     Месяц 6
До (Ручной): 16.2% ---- 14.8% ---- 15.5% (стабильно высокая аварийность)
                         |
                         ▼ Внедрение CI/CD, YAxUnit и Quality Gate
После (CI):              3.2% ----  1.4% ----  0.7% ----  0.6% (устойчивая надежность)

Риски и ограничения

Внедрение контейнеризированного CI/CD в среде 1С сопряжено со специфическими платформенными рисками, требующими строгого инженерного контроля.

1. Лицензирование платформы 1С при динамическом создании контейнеров

2. Риск разрастания времени выполнения тестов (Test Suite Bloat)

3. Антипаттерн «Тесты ради тестов» (False Sense of Security)


Итоги

Практические итоги внедрения контура непрерывного контроля качества

Внедрение автоматизированного конвейера непрерывной интеграции на базе Docker, YAxUnit и Quality Gates коренным образом трансформировало процессы разработки в «Торговом контуре»: 1. Ликвидирован класс регрессионных дефектов: Частота сбоев при поставках (Change Failure Rate) упала с 15.4% до менее 0.8%, защитив промышленный контур от критических простоев; 2. Время обратной связи сократилось до 7.5 минут: Разработчик узнает о синтаксических ошибках, нарушении стандартов кода или сломанной бизнес-логике проведения документов в течение нескольких минут после отправки ветки, не отвлекая коллег; 3. Исключены расходы на поддержание постоянных тестовых сред: Эфемерный запуск баз в tmpfs обеспечивает 100% воспроизводимость тестов при минимальной совокупной стоимости владения (TCO).

Переход к Этапу III (Архитектурное масштабирование)

С завершением Части 5 Этап II («Инженерная культура и DevOps») полностью сформирован: - В Части 4 мы организовали распределенное версионирование метаданных в Git и трехстороннее слияние; - В Части 5 мы защитили кодовую базу автоматизированным конвейером тестирования и Quality Gates.

Конвейер разработки стабилизирован, релизы защищены автотестами - мы переходим к Этапу III (Архитектурное масштабирование):

Часть 6A: «Изоляция высоконагруженного интеграционного контура через брокеры сообщений RabbitMQ». В следующей статье мы разберем отказ от синхронных HTTP/REST-вызовов при обмене данными с маркетплейсами и внешними WMS-системами, проектирование отказоустойчивой шины асинхронного обмена на базе RabbitMQ, гарантированную доставку сообщений и защиту транзакционного ядра 1C:ERP от пиковых внешних перегрузок.

Расширение «НОПик» для 1С — встраиваемый коннектор к внешнему AI с интеллектуальным поиском по базе. Задавайте вопросы обычными словами - AI сам найдёт нужное. 45 дней бесплатно.

Попробовать НОПик →
Знаете ответ на такие вопросы не хуже автора статьи? Пройдите бесплатную анонимную проверку уровня на infolimp.ru - 3 практических задачи, 15 минут, публичный токен-профиль, который можно показать работодателю или заказчику. Без регистрации по почте.

Проверить свой уровень бесплатно →