По специфике должности, чаще работаю с самописными конфигурациями - 8.3 в управляемых и обычных формах. Работаем в небольшом коллективе с разной степенью квалификации и (само-, без-)ответственности коллег. При этом иногда возникают проблемки при выкатывании нового функционала в продуктив. "Да как, я же все протестировал", говорит автор кода. Но, например, не учитывается обстоятельство, что тестирование проводилось под пользователем с максимальными привилегиями.
На устоявшихся конфигурациях таких изменений по добавлению нового функционала - достаточно мало. Но вот появилась задача по разработке еще одной конфигурации... К тому же, частенько интересовало, как самому написать дымовой тест - здесь имеются в виду TDD-тесты для xUnitFor1C/ADD.
В итоге, решил поразбираться и написать какой-то набор дымовых тестов, что-то проверяющих на основании метаданных конфигурации. Для этого выработал себе задачи-минимум, исходя из которых будут писаться инструменты:
- Проверка доступности Чтения сохраняемых данных (док-тов, справочников и т.п.) для ролей без расширенных полномочий
- Проверка установки свойства РежимУправленияБлокировкойДанных в значение, установленное для самой конфигурации
Вторая задача, на самом деле, носит больше академический характер. Но для выработки инструментов - вполне себе подходит.
Поехали
Как устроен дымовой тест - смотрим в реализации от Silverbulleters, \tests\smoke\тесты_ОткрытиеФормКонфигурации. Видим, что для наших задач все сводится к вызовам:
ПараметрыТеста = НаборТестов.ПараметрыТеста(...)
и
НаборТестов.Добавить(ИмяПроцедурыОбработчика, ПараметрыТеста, ЗаголовокТеста)
Ок. Поехали далее
По нашим исходным задачам, понимаем, что в каждом будущем тесте мы будем просматривать какие-то списки метаданных. Можно, конечно, написать какой-то типовой код формирования таких списков. И потом с модификациями копипастить этот код из теста в тест. Но мы ведь любим красоту и лаконичность:) Поэтому заморачиваемся далее. И пишем свой плагин, который будет перечислять наши требуемые метаданные и вызывать какую-то нами определяемую процедуру.
И вот плагин написан (кто не в курсе, это обычная внешняя обработка, только с определенными типовыми методами). Называется ИтераторМетаданных. Его интерфейс описан далее.
Свойства:
- ДопустимыеМетаданные (СписокЗначений). Заполняем этот список самими метаданными:
Итератор.ДопустимыеМетаданные.Добавить(Метаданные.Справочники.Контрагенты); // добавление конкретного объекта метаданных
Итератор.ДопустимыеМетаданные.Добавить(Метаданные.Документы); // добавление коллекции метаданных
- ИсключаемыеМетаданные (СписокЗначений). Заполняем его кодом, аналогичным выше. Либо не заполняем.
- ДополнятьЗависимымиОбъектами (Булево, Ложь по умолчанию). Можно, например, заполнить ДопустимыеМетаданные только документами. И дополнительно можем установить реквизит ДополнятьЗависимымиОбъектами в значение Истина. И тогда анализироваться будут не только документы, но и регистры, справочники и перечисления, которые участвуют в структуре метаданных документов в виде их реквизитов.
Методы:
- Процедура Инициализация(...). Это типовой для плагина метод. В нем сбрасываем все настройки Итератора - чистим списки метаданных, сбрасываем ДополнятьЗависимымиОбъектами в Ложь.
- Функция ДеревоМетаданных(), возвращает ДеревоЗначений. Дерево имеет одно поле ОбъектМетаданных и два уровня. На верхнем уровне (Дерево.Строки) имеем имена коллекций метаданных. Например, Справочник. На вложенном уровне (Дерево.Строки[0].Строки) имеем данные типа ОбъектМетаданных, относящиеся к родительской коллекции.
- Процедура Перечислить(Источник, ПриСледующемОбъектеМетаданных, ПриСледующемТипеМетаданных = Неопределено)
- Параметры:
- Источник - ОбработкаОбъект - Модуль, в котором будут вызываться процедуры-обработчики событий.
- ПриСледующемОбъектеМетаданных - Строка - Имя процедуры-обработчика при переходе на следующий объект метаданных.
- ПриСледующемТипеМетаданных - Строка, Неопределено - Имя процедуры-обработчика при переходе на следующий тип объектов метаданных.
- Интерфейс обработчиков - ПроцедураОбработчик(ОбъектМетаданных) или ПроцедураОбработчик(ОбъектМетаданных, Родитель)
Для построения дерева можно и не заполнять реквизит ДопустимыеМетаданные. В этом случае дерево будет заполнено максимальным количеством метаданных. Для этого используется приватная функция плагина ВсеКоллекцииМетаданных().
Пишем тесты
Использовать Итератор и строить дерево дымовых тестов - можно двумя способами. Раз уж мы изначально поставили задачу по написанию двух тестов, то и напишем их с разными подходами.
Вариант 1. Тест на чтение Не-Администраторами
Этот вариант задумывался как раз изначально. И, может быть, чуть сложнее, чем будет следующий вариант. В этом тесте будем использовать метод Итератор.Перечислить(...).
В стандартном методе обработки тестирования проинициализируем Итератор:
Процедура Инициализация(КонтекстЯдраПараметр) Экспорт
[Пропущено]
ИтераторМетаданных = КонтекстЯдра.Плагин("ИтераторМетаданных");
ИтераторМетаданных.Инициализация(КонтекстЯдраПараметр); // Сброс реквизитов плагина. Необходимо сделать, т.к. плагин уже мог быть инициализирован другой тестовой обработкой
ИтераторМетаданных.ДополнятьЗависимымиОбъектами = Истина; // В принципе, можно и не дополнять. Проверял работу этого флага.
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.Документы);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.Справочники);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.РегистрыСведений);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.Константы);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.РегистрыНакопления);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.ПланыВидовХарактеристик);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.Задачи);
ИтераторМетаданных.ДопустимыеМетаданные.Добавить(Метаданные.БизнесПроцессы);
// При ДополнятьЗависимымиОбъектами = Истина, в объектах проверки появляются и перечисления.
// Но настройки прав для перечислений - нет. Поэтому Перечисления исключаем.
ИтераторМетаданных.ИсключаемыеМетаданные.Добавить(Метаданные.Перечисления);
КонецПроцедуры
Посмотрим еще раз на интерфейсы callback-процедур для метода Итератор.Перечислить(...) и напишем один интерфейсный метод. Он будет отвечать и за обработку разделов, и за обработку объектов метаданных:
Процедура ПриСледующемОбъектеМетаданных(ОбъектМетаданных, Родитель) Экспорт
ЗаголовокОбщаяЧасть = "Проверка доступа на Чтение Не-Администраторами";
Если Родитель=Неопределено И ТипЗнч(ОбъектМетаданных)=Тип("Строка") Тогда
НаборТестов.НачатьГруппу(ЗаголовокОбщаяЧасть + " " + ОбъектМетаданных);
ИначеЕсли ОбъектМетаданных<>Неопределено Тогда
ПараметрыТеста = НаборТестов.ПараметрыТеста(ОбъектМетаданных, Родитель);
ЗаголовокТеста = "" + ОбъектМетаданных.ПолноеИмя() + ": " + ЗаголовокОбщаяЧасть;
НаборТестов.Добавить("Тест_ПроверитьНеАдминистраторскиеПраваНаЧтение", ПараметрыТеста, ЗаголовокТеста);
КонецЕсли;
КонецПроцедуры
И самый конечный метод, который будет тестировать объект метаданных:
Процедура Тест_ПроверитьНеАдминистраторскиеПраваНаЧтение(ОбъектМетаданных, Родитель) Экспорт
ЧтениеДоступно = Ложь;
Для Каждого ТекРоль Из Метаданные.Роли Цикл
Если ПривилегированныеРоли.Получить(ТекРоль)<>Неопределено Тогда
Продолжить;
КонецЕсли;
ПараметрыДоступаОбъекта = ПараметрыДоступа("Read", ОбъектМетаданных, , ТекРоль);
Если ПараметрыДоступаОбъекта.Доступность Тогда
ЧтениеДоступно = Истина;
Прервать;
КонецЕсли;
КонецЦикла;
Ожидаем.Что(ЧтениеДоступно).ЕстьИстина();
КонецПроцедуры
В этом методе используется переменная модуля ПривилегированныеРоли. Это Соответствие, которое заполняем ролями (Объект метаданных: Роль), обладающими расширенными полномочиями на чтение. Инициализируем и заполняем переменную также в стандартном методе Инициализация. Здесь этот код показывать особого смысла нет. См. в исходниках.
Вариант 2. Тест на проверку значений свойств РежимУправленияБлокировкойДанных
Как уже писал, это больше "академический" тест. В реальных разработках он будет иметь ценность лишь для конфигураций, которые находятся в стадии перевода с одного режима управления блокировками на другой.
Код уже не так интересен, и будет расположен в спойлерах.
Опять в стандартном методе обработки тестирования проинициализируем Итератор. Но уже немного по-другому.
А теперь для построения дерева тестов, воспользуемся функцией Итератор.ДеревоМетаданных().
И короткий тестовый метод для фреймворка.
Заключение
Итого, имеем новые знания и инструменты - разобрались, как писать плагины и дымовые тесты для фреймворков xUnitFor1C/ADD. И написали тесты.
Исходники плагина уже можно смотреть в ADD (ветка develop), по пути add/plugins/ИтераторМетаданных/. Тесты добавятся там же (или уже добавились), в ADD, по пути add/tests/smoke/.
Также все эти наработки можно скачать из прикрепленного к публикации файла. В нем небольшим бонусом еще есть файл с шаблонами кода для скелета тестовой обработки и с функциями текучих утверждений.
Про совместимость кода. На платформе 8.3 - будет работать в любой версии. Для запуска на платформе 8.2 нужно немного подрихтовать - закомментировать объявление областей кода (#Область/#КонецОбласти). БСП не используется.