Тесты в расширении конфигурации 1С: Vanessa ADD, xddTestRunner и CI в Jenkins
Приём 1. Зачем переносить тесты из EPF в расширение
Тема статьи: модульные тесты в расширениях конфигурации 1С:Предприятие 8.3, прогон через Vanessa ADD и xddTestRunner и включение того же набора в CI, в том числе Jenkins. Если вы уже писали хУнит-тесты во внешних обработках, задача здесь та же, но код лежит рядом с общими модулями конфигурации, а не в отдельном каталоге EPF.
В экосистеме 1С давно два привычных пути автотестов. Первый: тесты на языке 1С в стиле xUnit внутри Vanessa ADD. Второй: сценарии на Gherkin через Vanessa Automation. Gherkin удобен для UI, но это другой язык и другой контур сопровождения; для проверки функций общих модулей чаще берут код 1С.
У второго пути узкое место не в синтаксисе assert, а в подключении к проекту. Исторически внешний код в 1С жил во внешних обработках, и фреймворки, включая Vanessa ADD, умели обходить каталоги EPF, узнавать тестовые обработки и грузить их в раннер. Чем больше тестов, тем сильнее «epf hell»: параллель с dll hell, только вместо библиотек у вас десятки epf с самодельной иерархией.
Структурировать EPF по функциям редко получается: полного дерева функциональности у команды обычно нет. По подсистемам конфигурации тоже спотыкаемся: объекты «не входящие ни в одну подсистему» превращаются в длинный список, и всё равно приходится придумывать свою таксономию каталогов на диске.
С расширениями конфигурации (штатный механизм платформы 8.3) тестовый код становится частью метаданных: общие модули расширения, те же подсистемы, тот же поиск и сравнение/объединение, что у прикладного кода. Главная мысль простая: тесты в расширении - это те же общие модули, что вы и так сопровождаете; Vanessa ADD даёт тонкую обвязку регистрации, а xddTestRunner и CI подхватывают их без зоопарка обработок.
Я выгружаю расширение Tests в тот же репозиторий, что и cf; на схеме видно, куда на диске ложатся модули по сравнению с каталогом epf.

Репозиторий: cf, расширение Tests и модули.bsl
На схеме выше я сознательно противопоставил два способа хранения: бинарные EPF в сторонней папке и исходники расширения Tests рядом с конфигурацией. Второй вариант не отменяет дисциплины в Git, зато дерево тестов в раннере повторяет дерево общих модулей, а не список файлов epf.
Отдельное тестовое расширение (имя вроде Tests или Тест, префикс имён Тест) я оформляю как addon/patch по правилам вашей поставки. В продуктивной базе его можно не подключать; для Jenkins достаточно СИ-базы, куда расширение подключается всегда.
Порядок применения при обновлении основной конфигурации тот же, что у любого расширения: сначала платформа и конфигурация, затем расширения, затем проверка заимствований.
Приём 2. Заимствование модуля и заглушки через &Вместо
Расширение для тестов опирается на два механизма платформы, без общей лекции про все возможности расширений. Первый: заимствование существующего общего модуля конфигурации в расширение с префиксом, например Тест.

Контекст: Приём 2. Заимствование модуля и заглушки через &Вместо
После заимствования модуль в расширении видит прикладной код так, как нужно для white-box проверок: доступны экспортные функции и процедуры, и внутренние, неэкспортные, если вы вызываете их из тестового модуля в том же расширении по правилам заимствования. Так проверяется логика, которую снаружи модуля не вызовешь.
Второй механизм: переопределение методов &Вместо и &После. Для модульных тестов это штатный способ заглушить HTTP, SOAP, почту или обращение к файловой системе без правок основной конфигурации. Один раз показываю паттерн на условном общем модуле обмена с НТТР-сервисом.
Чтобы не тянуть реальный HTTP в унит-прогон, фиксирую обмен на схеме: прикладной вызов остаётся прежним, подменяется только транспорт в расширении Tests.

Заглушка HTTP в расширении Tests
На диаграмме запрос «уходит» в заглушку расширения, а не во внешний сервис. В модуле расширения процедура с &Вместо возвращает заранее подготовленную структуру или JSON, который разбирает прикладной код. Боевое расширение интеграций и тестовое лучше не смешивать: заглушки живут только в Tests, иначе на CI вы рискуете подменить поведение там, где нужен реальный обмен.
Пример фрагмента заглушки (имена модулей подставьте свои):
&Вместо("ВыполнитьHTTPЗапрос")
Функция Тест_ВыполнитьHTTPЗапрос(ПараметрыЗапроса)
Ответ = Новый Структура;
Ответ.Вставить("КодСостояния", 200);
Ответ.Вставить("Тело", "{""status"":""ok""}");
Возврат Ответ;
КонецФункции
Тело ответа для обмена можно держать в макете или JСОН-файле выгрузки расширения; для статьи достаточно показать, что возвращается структура, которую ждёт прикладной разбор.
{
"status": "ok",
"items": [
{"id": "00000000-0000-0000-0000-000000000001", "qty": 1}
]
}
Приём 3. Минимальный проект: первый тест в общем модуле расширения
Беру учебный контур: один общий модуль прикладной логики с функцией проверки ссылки, условно СсылкаСуществует(Ссылка). Справочник может быть любым; важна сама функция, которая отличает битую ссылку от существующей.

Контекст: Приём 3. Минимальный проект: первый тест в общем модуле расширения
Шаги в конфигураторе или EDT: создать расширение Tests, задать префикс Тест, заимствовать общий модуль, где лежит СсылкаСуществует. Добавить общий модуль Тест_ИмяПрикладногоМодуля (сервер, вызов сервера по вашим правилам).
Проверка на несуществующий UUID: формируем ссылку с заведомо несуществующим идентификатором и ждём Ложь. Так отсекаем регресс, когда функция начинает «верить» любой ссылке.
Функция Тест_СсылкаСуществует_НесуществующийИдентификатор() Экспорт
Идентификатор = Новый УникальныйИдентификатор("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee");
НесуществующаяСсылка = Справочники.Номенклатура.ПолучитьСсылку(Идентификатор);
Результат = ИмяПрикладногоМодуля.СсылкаСуществует(НесуществующаяСсылка);
Возврат Результат;
КонецФункции
Имя справочника замените на свой объект метаданных; метод ПолучитьСсылку по UUID - штатный способ получить ссылку без записи в базе. Локально, до Vanessa ADD, можно временно вызвать функцию из консоли кода в той же СИ-базе и убедиться, что возвращается Ложь.
Если функция ходит в запрос, имеет смысл один раз руками выполнить тот же запрос с параметром &Ссылка для несуществующего значения и убедиться, что выборка пустая.
Запрос ниже повторяет типичную проверку «есть ли объект в базе» по ссылке; параметр передаём явно, без выборки всех полей.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка = &Ссылка
Для несуществующего UUID результат пустой; прикладная функция должна интерпретировать это как «ссылки нет». Если у вас другой справочник, замените имя таблицы в запросе.
Приём 4. Обвязка Vanessa ADD: ЗаполнитьНаборТестов и утверждения
Код проверки почти не меняется по сравнению с ЕРF-тестом; меняется форма регистрации. В общем модуле расширения добавляем процедуру ЗаполнитьНаборТестов, как того требует Vanessa ADD.

Контекст: Приём 4. Обвязка Vanessa ADD: ЗаполнитьНаборТестов и утверждения
Процедура ЗаполнитьНаборТестов(НаборТестов, КонтекстВанесса) Экспорт
НаборТестов.Добавить("Тест_СсылкаСуществует_НесуществующийИдентификатор");
КонецПроцедуры
Контекст Ванесса передаётся вторым параметром: через него подключаются плагины. Для утверждений я подключаю «БазовыеУтверждения» и в теле теста оставляю две содержательные строки плюс одну служебную функцию на модуль.
Процедура Тест_СсылкаСуществует_НесуществующийИдентификатор(КонтекстВанесса) Экспорт
Результат = Тест_СсылкаСуществует_НесуществующийИдентификаторЗначение();
КонтекстВанесса.Плагин("БазовыеУтверждения").Проверить(Не Результат);
КонецПроцедуры
Функция Тест_СсылкаСуществует_НесуществующийИдентификаторЗначение()
// вычисление результата, как в предыдущем фрагменте
Возврат Ложь;
КонецФункции
Проверить(Не Результат) читается как «ожидаем, что ссылка не существует». Если плагин не найден, раннер упадёт при загрузке; это быстрее, чем silent pass. Сравнение с EPF: раньше та же процедура жила в обработке, теперь в.bsl рядом с заимствованным модулем.
Имена процедур в наборе должны совпадать с экспортными процедурами тестов; опечатка в строке Добавить даёт «тест прошёл мимо» без выполнения тела.
Приём 5. Загрузка и прогон в xddTestRunner
Открываю xddTestRunner (клиент тестирования или связка с Vanessa ADD, как настроено у вас). Ищу пункт загрузки тестов из расширения конфигурации, формулировка в интерфейсе может чуть отличаться по версии ADD.

Контекст: Приём 5. Загрузка и прогон в xddTestRunner
Дальше выбор: загрузить одно расширение Tests или обойти все общие модули конфигурации. Для daily work я выбираю расширение: меньше шума, дерево группируется по общим модулям так же, как в метаданных.
После загрузки в дереве вижу узлы общих модулей расширения и зарегистрированные тесты. Запускаю прогон; красный тест смотрю по стеку и сообщению БазовыхУтверждений. Зелёный прогон в IDE - только половина цели; вторую половину закрывает CI.
Если тест не появился, проверяю: процедура ЗаполнитьНаборТестов экспортная, имя теста совпадает с добавленным в набор, расширение подключено к базе, модуль серверный там, где нужен серверный контекст.
Практический выигрыш для сопровождения: рефакторинг общего модуля и его тестового двойника в одном расширении идёт одним поиском по конфигурации; подсистемы Tests видны в дереве метаданных, а не только в README каталога epf.
Приём 6. Сопровождение в Git, отдельная IB и цепочка Jenkins
Общие модули расширения выгружаются в Git или EDT рядом с основной конфигурацией: каталог расширения, файлы модулей.bsl, manifest расширения. EPF в репозитории как бинарники хуже диff-ятся и хуже стыкуются с code review.
Для прогона использую отдельную информационную базу. Параметры запуска клиента тестирования и xddTestRunner задаю явно: сервер, база, пользователь, пароль через безопасное хранилище Jenkins. При необходимости базу сбрасываю из dt или поднимаю заново штатными средствами администрирования платформы, без самодельных «скриптов заказчика» в качестве единственного способа.
Для Jenkins мне важнее цикл целиком, чем вид job в UI; ниже порядок шагов от commit до exit code.

CI: выгрузка, IB, xddTestRunner, exit code
Цикл на схеме - тот же объект автоматизации, что вы грузите руками: сборка/выгрузка конфигурации и расширения, развёртывание IB, запуск Vanessa Automation, vanessa-runner или аналога из командной строки, код возврата job как критерий успеха. Конкретный Jenkinsfile зависит от ваших агентов и способа выгрузки; магии в самом Jenkins нет, важно, что раннер после «Загрузить тесты из расширения» видит тот же набор.
Типовая последовательность шагов job, которую я закладываю в pipeline: checkout репозитория; compile/load cf и cfe расширения в СИ-базу; запуск раннера с ключами non-interactive; архив junit или текстового лога при поддержке вашей версии ADD. Успех = нулевой exit code и отсутствие упавших тестов в отчёте.
Изоляция данных: не гоняю тесты на копии прода с живыми персональными данными. Отдельная IB с урезанным набором или с демо-дт экономит время на разбор инцидентов «тест изменил документ».
На агенте Jenkins я фиксирую версию платформы и список подключённых расширений в логе job; иначе «зелёный вчера» не воспроизводится, если агент обновили без вашего ведома.
Приём 7. Границы подхода и где чаще ошибаются
Тесты в расширении не отменяют клиентский или тонкий контекст для УИ-сценариев. Модульные и многие интеграционные проверки общих модулей - зона расширения плюс ADD; Gherkin и Vanessa Automation остаются для сценариев интерфейса. Смешивать в одном разделе инструкции не буду: это разные жанры прогона.
Полный отказ от EPF для всех видов тестов не обещаю. Обработка может жить, если так исторически устроен раннер или если тест по смыслу - отчёт. Цель - убрать epf hell для модульных тестов прикладных общих модулей.
При обновлении основной конфигурации заимствования могут потребовать повторного принятия изменений в расширении Tests. Я после каждого обновления конфигурации прогоняю compare расширения и один смоке-прогон xddTestRunner на СИ-базе.
Частый промах: забыть отключить тестовое расширение на продуктивной базе не только «на словах», но в списке подключённых расширений. Второй: заглушки &Вместо в боевом ратсн-расширении. Третий: гонять тесты на базе, где нет нужных метаданных основной конфигурации.
Вопрос: можно ли оставить старые ЕРF-тесты и параллельно расширение? Ответ: да, на переходный период; xddTestRunner позволяет грузить и обработки, и расширение. Я постепенно переношу модули, начиная с общих модулей с чистой логикой.
Вопрос: нужен ли EDT или хватит конфигуратора? Ответ: для статьи достаточно любого способа править расширение; в Гит-командах удобнее EDT, но механизм заимствования тот же.
Вопрос: что считать готовностью к CI? Ответ: один и тот же набор из расширения проходит локально в xddTestRunner и на агенте Jenkins без ручного клика «загрузить» в GUI; падение job воспроизводимо при сломанном тесте.
Вступайте в нашу телеграмм-группу Инфостарт
Вы можете заказать платную адаптацию этой статьи под ваши задачи на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.