Unit-тесты для кода 1С 8.3 на практике: от первой проверки до отчёта
Содержание
Чтобы сделать тест в 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: тесты лежат в расширении, основную конфигурацию трогать не нужно, а синтаксис проверок читается как обычная фраза.
Подготовка: тестовая база и расширение с тестами
Никогда не запускайте тесты на рабочей базе. Тест создаёт и удаляет данные, и одна ошибка в нём испортит реальный учёт.
Порядок подготовки:
- 🗄️ Сделайте копию рабочей базы через выгрузку
.dtили поднимите пустую базу из файла конфигурации.cf. Пустая база с минимумом справочников лучше: тесты не зависят от чужих данных. - 📦 Скачайте с GitHub-страницы проекта YAxUnit файл расширения
.cfeиз раздела релизов. Минимальную версию платформы смотрите в описании релиза. - 🔌 Загрузите расширение в тестовую базу. В Конфигураторе:
Конфигурация → Расширения конфигурации, добавить и загрузить из файла. Затем примените изменения к базе. - 🧩 Создайте второе, своё расширение — например,
Тесты. В нём будут только ваши тестовые модули. Фреймворк и тесты лучше не смешивать: так фреймворк обновляется заменой одного файла. - ⚙️ В свойствах своего расширения отключите безопасный режим и защиту от опасных действий, если тестам нужен доступ к объектам базы. Иначе часть вызовов упадёт с ошибкой прав.
Расширение Тесты должно подходить к основной конфигурации. Если вы проверяете метод общего модуля, у этого модуля стоит флаг доступа нужного контекста: тест на сервере не увидит модуль, который компилируется только на клиенте.
Если разработка ведётся в 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.
Что смотреть в первую очередь:
- ❌ Упавшие тесты (failure) — проверка не прошла, код работает не так, как ожидалось.
- 💥 Ошибки (error) — тест сломался до проверки: нет модуля, нет прав, ошибка в самом тесте.
- ⏱️ Время — тест, который идёт 30 секунд, скорее всего, лезет в большие объёмы данных и требует пересмотра.
💡 Error почти всегда означает проблему в тесте или окружении, а не в проверяемом коде. Сначала разберитесь с ними: пока тест не запускается, его зелёный или красный результат ничего не говорит.
Типичные ошибки при тестировании в 1С
Ещё несколько привычек, которые портят пользу от тестов:
- 🙈 Тест без проверки. Процедура вызывает функцию, но не содержит ни одного
ОжидаетЧто. Такой тест всегда зелёный и ничего не ловит. - 📋 Ожидание скопировано из результата. Запустили функцию, увидели 1199,99 и вписали это число в тест. Ожидаемое значение считается вручную, по условию задачи.
- 🐘 Гигантские тесты. Одна процедура проводит десять документов и проверяет отчёт. Упадёт — придётся час искать, на каком шаге.
- 🕰️ Текущая дата в проверках. Тест с
ТекущаяДата()проходит сегодня и падает в последний день месяца. Передавайте дату параметром.
С чего начать на живом проекте
Не пытайтесь покрыть тестами всю конфигурацию. Начните с общих модулей с расчётами: цены, скидки, налоги, сроки. Это чистые функции, их легко тестировать, а ошибки в них стоят дороже всего. Потом добавляйте тест на каждую найденную ошибку: сначала воспроизведите её тестом, затем исправьте код.