Unit-тесты для кода 1С 8.3 на практике: от первой проверки до отчёта

· 10 мин

Содержание

Чтобы сделать тест в 1С 8.3, подключите фреймворк модульного тестирования (чаще всего YAxUnit) как расширение конфигурации . В расширении создайте общий модуль с тестами, зарегистрируйте их в процедуре ИсполняемыеСценарии() и проверяйте результат через ЮТест.ОжидаетЧто(...). Запускается всё в отдельной тестовой базе — из EDT или командной строкой.

Поменяли одну функцию расчёта, а через неделю всплыл неверный итог в отчёте, который её вызывает. Unit-тестирование ловит такое за секунды, до того как код попадёт к пользователям. Достаточно тестовой базы, двух расширений и десятка строк кода на каждую функцию.

Чем тестировать код 1С: выбор инструмента

В типовой поставке платформы нет готового фреймворка для модульных тестов. Платформа даёт механизм автоматизированного тестирования интерфейса: клиент тестирования и менеджер тестирования (параметры запуска /TESTCLIENT и /TESTMANAGER). Это проверка «через экран», а не проверка отдельной функции.

Для unit-тестов используют открытые фреймворки сообщества:

ИнструментЧто проверяетГде пишутся тестыКогда выбирать
YAxUnitфункции и процедуры модулейрасширение конфигурацииосновной выбор для unit-тестов, удобен с EDT
Vanessa ADDмодули и сценариивнешние обработки .epfпроект уже живёт на Vanessa ADD и vanessa-runner
Vanessa Automationповедение интерфейсасценарии на языке Gherkinприёмочные и регрессионные тесты форм
Самописная обработкапростые функциивнешняя обработкаразово проверить пару функций без фреймворка

💡 Разделяйте уровни. Unit-тест проверяет одну функцию на конкретных входных данных и работает за доли секунды. Сценарный тест через интерфейс проверяет весь путь пользователя, но идёт минуты и ломается от перестановки поля на форме.

Дальше примеры на YAxUnit: тесты лежат в расширении, основную конфигурацию трогать не нужно, а синтаксис проверок читается как обычная фраза.

Подготовка: тестовая база и расширение с тестами

Никогда не запускайте тесты на рабочей базе. Тест создаёт и удаляет данные, и одна ошибка в нём испортит реальный учёт.

Порядок подготовки:

  1. 🗄️ Сделайте копию рабочей базы через выгрузку .dt или поднимите пустую базу из файла конфигурации .cf. Пустая база с минимумом справочников лучше: тесты не зависят от чужих данных.
  2. 📦 Скачайте с GitHub-страницы проекта YAxUnit файл расширения .cfe из раздела релизов. Минимальную версию платформы смотрите в описании релиза.
  3. 🔌 Загрузите расширение в тестовую базу. В Конфигураторе: Конфигурация → Расширения конфигурации, добавить и загрузить из файла. Затем примените изменения к базе.
  4. 🧩 Создайте второе, своё расширение — например, Тесты. В нём будут только ваши тестовые модули. Фреймворк и тесты лучше не смешивать: так фреймворк обновляется заменой одного файла.
  5. ⚙️ В свойствах своего расширения отключите безопасный режим и защиту от опасных действий, если тестам нужен доступ к объектам базы. Иначе часть вызовов упадёт с ошибкой прав.

Расширение Тесты должно подходить к основной конфигурации. Если вы проверяете метод общего модуля, у этого модуля стоит флаг доступа нужного контекста: тест на сервере не увидит модуль, который компилируется только на клиенте.

Если разработка ведётся в 1С:EDT, расширение с тестами удобно держать отдельным проектом в том же рабочем пространстве. Для EDT есть плагин YAxUnit: он запускает тесты из контекстного меню и показывает результат в отдельной панели.

Как написать первый тест в 1С: структура модуля

Возьмём простую функцию в общем модуле Расчеты основной конфигурации:

Функция СуммаСНДС(Сумма, Ставка) Экспорт

	Если Сумма < 0 Тогда
		ВызватьИсключение "Сумма не может быть отрицательной";
	КонецЕсли;

	Возврат Сумма + Окр(Сумма * Ставка / 100, 2);

КонецФункции

В расширении Тесты создайте общий модуль, например ОМ_Расчеты. Префикс в имени — договорённость команды. Он помогает с первого взгляда отличить тестовый модуль от рабочего. Поставьте модулю флаг «Сервер», раз проверяемая функция серверная.

Тестовый модуль состоит из двух частей:

  • 📝 экспортная процедура ИсполняемыеСценарии() — список тестов модуля;
  • 🧪 экспортные процедуры-тесты — по одной на каждый проверяемый случай.
Процедура ИсполняемыеСценарии() Экспорт

	ЮТТесты
		.ДобавитьТест("СуммаСНДС_Ставка20")
		.ДобавитьТест("СуммаСНДС_НулеваяСтавка")
		.ДобавитьТест("СуммаСНДС_Округление");

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

Процедура СуммаСНДС_Ставка20() Экспорт

	Результат = Расчеты.СуммаСНДС(1000, 20);
	ЮТест.ОжидаетЧто(Результат).Равно(1200);

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

Процедура СуммаСНДС_НулеваяСтавка() Экспорт

	Результат = Расчеты.СуммаСНДС(1000, 0);
	ЮТест.ОжидаетЧто(Результат).Равно(1000);

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

Процедура СуммаСНДС_Округление() Экспорт

	// 333,33 * 20 / 100 = 66,666 -> 66,67
	Результат = Расчеты.СуммаСНДС(333.33, 20);
	ЮТест.ОжидаетЧто(Результат).Равно(400);

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

Пересчитаем последний пример: 333,33 + 66,67 = 400,00. Тест упадёт, если кто-то уберёт Окр() и функция вернёт 399,996.

Как называть тесты

Имя теста — это строка в отчёте. Через полгода по нему нужно понять, что сломалось, не открывая код. Удобная схема: ЧтоПроверяем_ПриКакихУсловиях. Например, СуммаСНДС_ОтрицательнаяСумма или ПроведениеРеализации_БезСклада.

Правило «одна проверка — один смысл»

Один тест — один сценарий. В тесте может быть несколько вызовов ОжидаетЧто, если они проверяют один результат с разных сторон: тип, заполненность, значение. Но проверять в одной процедуре и нулевую ставку, и округление не стоит. Упадёт первая проверка — вторая не выполнится, и вы не узнаете, работает ли она.

Кроме Равно у утверждений есть и другие проверки: на заполненность, на тип значения, на сравнение «больше/меньше», на вхождение в коллекцию. Полный перечень приведён в документации YAxUnit, а в EDT его подсказывает контекстная помощь после точки.

Тестовые данные и изоляция тестов от базы

Функции-«калькуляторы» тестировать легко. Сложнее с кодом, который читает справочники, проводит документы и пишет в регистры. Здесь главная задача — чтобы тесты не зависели друг от друга и от того, что лежит в базе.

Три способа изоляции

СпособКак работаетОграничение
Транзакция с откатомтест выполняется в транзакции, после него изменения откатываютсятолько серверные тесты; код с собственными транзакциями ведёт себя иначе
Создание и удаление данныхтест сам создаёт нужные элементы и удаляет их в концепри падении теста мусор может остаться
Чистая база перед прогономбаза каждый раз разворачивается из эталонного .dtдольше; нужен скрипт развёртывания

В YAxUnit выполнение в транзакции включается настройкой при регистрации тестов — методом ВТранзакции() в цепочке вызовов ЮТТесты. Настройку можно задать для всего модуля или для отдельного теста.

Процедура ИсполняемыеСценарии() Экспорт

	ЮТТесты
		.ВТранзакции()
		.ДобавитьТест("ЗаписьКонтрагента_ЗаполняетсяКодПоУмолчанию");

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

Процедура ЗаписьКонтрагента_ЗаполняетсяКодПоУмолчанию() Экспорт

	Элемент = Справочники.Контрагенты.СоздатьЭлемент();
	Элемент.Наименование = "Тестовый контрагент";
	Элемент.Записать();

	ЮТест.ОжидаетЧто(Элемент.Код).Заполнено();

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

После теста транзакция откатывается, и контрагент в базе не остаётся.

💡 Не используйте в тестах «Ссылку на существующий элемент по наименованию». Тест, который ищет контрагента «Ромашка», сломается в первой же базе без «Ромашки». Нужные данные тест создаёт сам.

Случайные значения

В YAxUnit есть модуль ЮТест.Данные() — генераторы случайных строк, чисел и вспомогательные методы для создания тестовых объектов. Случайное наименование защищает от конфликта уникальности, когда два теста создают элемент с одним именем.

Зависимости от внешнего мира

Если функция обращается к веб-сервису, файлу или текущей дате, её трудно проверить честно. Есть два пути:

  • 🔧 вынести «внешнюю» часть в отдельную функцию и передавать результат параметром — тогда тестируется логика, а не сеть;
  • 🎭 подменить вызов на время теста. В составе YAxUnit для этого есть модуль «Мокито». Он «обучает» метод вернуть заданное значение вместо реального вызова.

Негативные сценарии: проверка исключений

Проверить, что функция работает на хороших данных, — половина дела. Вторая половина — убедиться, что на плохих данных она падает правильно, с понятным текстом, а не тихо возвращает мусор.

Для проверки исключения вызов передаётся во фреймворк, а не выполняется напрямую. Иначе исключение прервёт сам тест:

Процедура СуммаСНДС_ОтрицательнаяСумма() Экспорт

	ЮТест.ОжидаетЧто(Расчеты)
		.Метод("СуммаСНДС")
		.Параметр(-5)
		.Параметр(20)
		.ВыбрасываетИсключение("Сумма не может быть отрицательной");

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

Фреймворк сам вызовет Расчеты.СуммаСНДС(-5, 20), перехватит исключение и сверит текст. Если исключения не будет, тест упадёт с сообщением, что ожидалась ошибка.

Не забудьте добавить имя СуммаСНДС_ОтрицательнаяСумма в ИсполняемыеСценарии(). Незарегистрированная процедура не запустится, даже если она экспортная.

Какие негативные случаи стоит покрыть:

  • ⛔ пустые параметры: Неопределено, пустая ссылка, пустая строка;
  • ➖ отрицательные и нулевые числа там, где они запрещены;
  • 📏 граничные значения: 0, максимальная длина строки, дата начала периода;
  • 🔁 повторный вызов: проведение уже проведённого документа, повторная запись;
  • 🔒 недостаточно прав, если функция проверяет роль пользователя.

Проверяйте текст исключения целиком или его ключевую часть. Тест «упало хоть как-нибудь» пропустит ошибку деления на ноль там, где ждали понятное сообщение для пользователя.

Запуск тестов и анализ результатов

Из EDT

С установленным плагином YAxUnit тесты запускаются из контекстного меню модуля или проекта. Результаты появляются в панели тестов: зелёные прошли, красные упали, у каждого упавшего видно ожидаемое и фактическое значение и стек вызова.

Из командной строки

Для автоматического прогона базу запускают в режиме Предприятия с параметром /C и файлом настроек в формате JSON:

"C:\Program Files\1cv8\8.3.24.1342\bin\1cv8c.exe" ENTERPRISE /F"D:\Bases\TestBase" /N"Тестировщик" /P"" /C"RunUnitTests=D:\Tests\params.json"

Путь к платформе и версию подставьте свои. Пример файла настроек:

{
  "filter": {
    "extensions": ["Тесты"]
  },
  "reportFormat": "jUnit",
  "reportPath": "D:\\Tests\\report",
  "closeAfterTests": true
}

Фильтр ограничивает прогон одним расширением. Отчёт в формате jUnit сохраняется в указанный каталог, а closeAfterTests закрывает сеанс после прогона. Полный список параметров (фильтр по модулям и тестам, формат лога) смотрите в документации проекта.

Как читать отчёт

jUnit-отчёт — это XML, который понимают системы непрерывной интеграции: Jenkins, GitLab CI, TeamCity. Они показывают динамику по сборкам. Для наглядного отчёта с историей подойдёт Allure.

Что смотреть в первую очередь:

  1. ❌ Упавшие тесты (failure) — проверка не прошла, код работает не так, как ожидалось.
  2. 💥 Ошибки (error) — тест сломался до проверки: нет модуля, нет прав, ошибка в самом тесте.
  3. ⏱️ Время — тест, который идёт 30 секунд, скорее всего, лезет в большие объёмы данных и требует пересмотра.

💡 Error почти всегда означает проблему в тесте или окружении, а не в проверяемом коде. Сначала разберитесь с ними: пока тест не запускается, его зелёный или красный результат ничего не говорит.

Типичные ошибки при тестировании в 1С

Диагностика: выберите симптом

Ещё несколько привычек, которые портят пользу от тестов:

  • 🙈 Тест без проверки. Процедура вызывает функцию, но не содержит ни одного ОжидаетЧто. Такой тест всегда зелёный и ничего не ловит.
  • 📋 Ожидание скопировано из результата. Запустили функцию, увидели 1199,99 и вписали это число в тест. Ожидаемое значение считается вручную, по условию задачи.
  • 🐘 Гигантские тесты. Одна процедура проводит десять документов и проверяет отчёт. Упадёт — придётся час искать, на каком шаге.
  • 🕰️ Текущая дата в проверках. Тест с ТекущаяДата() проходит сегодня и падает в последний день месяца. Передавайте дату параметром.

С чего начать на живом проекте

Не пытайтесь покрыть тестами всю конфигурацию. Начните с общих модулей с расчётами: цены, скидки, налоги, сроки. Это чистые функции, их легко тестировать, а ошибки в них стоят дороже всего. Потом добавляйте тест на каждую найденную ошибку: сначала воспроизведите её тестом, затем исправьте код.

Частые вопросы

Можно ли писать тесты без расширения конфигурации?
Да. Vanessa ADD хранит тесты во внешних обработках, а простую проверку можно сделать своей обработкой с ВызватьИсключение при неверном результате. Но без фреймворка вы теряете отчёт, фильтры и запуск в CI.
Нужна ли 1С:EDT для тестов на YAxUnit?
Нет. Расширение с тестами можно вести в Конфигураторе и запускать командной строкой. EDT добавляет удобство: запуск из редактора, панель результатов, подсказки по методам проверок.
Как протестировать клиентский код?
Тестовый модуль должен компилироваться на клиенте — поставьте ему флаг «Клиент (управляемое приложение)». Транзакция с откатом на клиенте недоступна, поэтому данные, созданные в таком тесте, удаляйте вручную.
Чем unit-тест отличается от сценарного теста?
Unit-тест вызывает функцию напрямую и проверяет её результат. Сценарный тест имитирует действия пользователя на формах: открыть документ, заполнить поля, провести. Первые быстрые и точные, вторые проверяют систему целиком.
Сломаются ли тесты при обновлении типовой конфигурации?
Тесты в отдельном расширении обновление не затрагивает. Но если изменилась сигнатура или поведение проверяемой функции, тест упадёт — и это нормально: он показывает, где поведение изменилось.