До релиза новой версии фреймворка по тестированию “xUnitFor1C” осталось совсем немного, а значит пришло время рассказать о проделанной работе и о том, что ожидает пользователей.
Релиз получится действительно мажорным, изменений очень много, и они носят глобальный характер. Но обо всем по порядку.
Насколько я понимаю, на момент, когда проект только рождался, основной целью было понять, насколько модульное тестирование может быть востребовано в среде 1С. Понятное дело, что продумывать и выделять уровни абстракций на этапе прототипирования — дело не особо перспективное. Прототип не обладает эластичностью настоящего кода. Прототип — это эксперимент, результаты которого нужно выбрасывать.
Далее флаг разработки переходил от одного энтузиаста к другому, при этом базовая архитектура оставалась прежней. С ростом популярности продукта и осознания того, что хотелось бы получить, стало все сложнее вносить изменения.
Мне нравится метафора Алана Купера:
Архитектура, существовавшая с прототипа, была тем самым отклонением на 5-ом кирпиче, которая не позволяла добавить новый функционал, который нам так хотелось — тестирование в стиле BDD, в частности, использование Gherkin. Есть мнение, что нужно выбирать что-то одно. Либо TDD либо BDD. Поработав по принципам гибкого тестирования, я понял, что модульные и сценарные тесты ни в коем случае не должны противопоставляться, они прекрасное дополнение друг к другу!
Вот так выглядит матрица гибкого тестирования:
Модульные тесты относятся к квадранту 1 — низкоуровневые тесты. Предназначены для проектирования слабо-связанной, эластичной, тестируемой архитектуры. Они выполняют для разработчика ту же роль, что и страховочный трос для скалолаза. При этом они достаточно дешевые в разработке и содержании.
Сценарные тесты относятся к квадранту 2 — тесты более высокого уровня. Нужны, чтобы убедится, что мы правильно понимаем то, что нужно сделать. Это мостик между бизнесом и разработкой, возможность говорить на одном языке. Прекрасная документация, которая никогда не устареет. При этом они дороже в создании и более хрупкие.
По мне так прекрасная синергия. Я считаю, что для создания высококачественного ПО нужно использовать оба подхода вместе! Совмещенные тесты обоих видов создают отличную защиту от регрессии.
Она полностью переработана. Инструмент стал намного гибче и проще в разработке. Теперь это не обработка с 8,5к строк кода, теперь это ядро с системой плагинов. Да-да, тут нет ошибки, в 1С можно сделать фреймворк, который будет расширяться плагинами.
В качестве плагинов выступают внешние обработки, располагающиеся в папке “Plugins” и реализующие следующий базовый интерфейс:
Обращение к плагину происходит по идентификатору, например:
Работу ядра теперь можно грубо выразить в виде единственной функции:
Возникает резонный вопрос: “Что за канонические дерево тестов и результат тестирования?”.
Каноническое дерево тестов позволяет построить универсальную “запускалку” тестовых методов. Представляет собой древовидную структуру данных. Состоит из узлов следующих видов:
Канонический результат тестирования очень похож на дерево тестов с тем отличием, что содержит дополнительную информацию о результатах выполнения:
Построением деревьев с тестами занимается специальный тип плагинов “Загрузчик”. Типовой сценарий работы с загрузчиками:
API загрузчика:
Функция ВыбратьПутьИнтерактивно(ТекущийПуть = "") — клиентский метод для интерактивного выбора пути для загрузки тестов;
Функция Загрузить(КонтекстЯдра, Путь) — поиск тестовых сценариев и построение канонического дерева тестов;
Функция ПолучитьКонтекстПоПути(КонтекстЯдра, Путь) — получение тестового контекста по переданному пути.
На текущий момент реализовано 3 базовых загрузчика:
После того как ядро переработало дерево тестов в результат тестирования, оно его передает еще одному специальному типу плагина “ГенераторОтчета”.
Генераторы отчетов трансформируют канонические результаты тестирования в любое другое представление. Например, сейчас реализованы:
API генератора отчета:
Функция СоздатьОтчет(КонтекстЯдра, РезультатыТестирования) — формирование отчета в формате предлагаемом плагином. Результаты тестирования передаются плагину в каноническом для ядра формате;
Процедура Показать(Отчет) — клиентский метод для интерактивного показа сформированного отчета. Плагин сам определяет, как именно нужно оформить для пользователя сформированный им отчет;
Процедура Экспортировать(Отчет, ПолныйПутьФайла) — сохраняет отчет по указанному пути, в основном используется для целей CI.
Всякие “полезняшки” имеют тип плагина “Утилита”. Как правило, выполняют библиотечную или сервисную функцию. Например, библиотека утверждений в стиле BDD — плагин “УтвержденияBDD”, о котором я писал в своей прошлой статье. “СериализаторMXL” — сервисный плагин, который занимается сериализацией данных базы в moxel-формат и обратно.
До влития в основной ствол разработки, исходники доступны по ссылке.
Теперь можно утверждать, что фреймворк вырос из “просто инструмент для модульного тестирования” в “инструмент позволяющий покрыть почти все возможные виды автоматического тестирования”. Он модульный, четко разделен по слоям, легко может дорабатываться и расширяться. Кажется, текущее название перестало передавать назначение продукта. Может, пришло время изменить название? Если у вас есть хорошая идея для названия, не стесняйтесь поделиться.
Релиз получится действительно мажорным, изменений очень много, и они носят глобальный характер. Но обо всем по порядку.
Зачем все перепиливать?
Насколько я понимаю, на момент, когда проект только рождался, основной целью было понять, насколько модульное тестирование может быть востребовано в среде 1С. Понятное дело, что продумывать и выделять уровни абстракций на этапе прототипирования — дело не особо перспективное. Прототип не обладает эластичностью настоящего кода. Прототип — это эксперимент, результаты которого нужно выбрасывать.
Далее флаг разработки переходил от одного энтузиаста к другому, при этом базовая архитектура оставалась прежней. С ростом популярности продукта и осознания того, что хотелось бы получить, стало все сложнее вносить изменения.
Мне нравится метафора Алана Купера:
Создание большой программы можно сравнить с постройкой столба из кирпича. Этот столб состоит из тысячи кирпичей, положенных один на другой. Столб может быть выстроен, только если класть кирпичи с большой точностью. Любое отклонение приведет к падению кирпичей. Если кирпич с номером 998 сможет отклонить на пять миллиметров, столб, вероятно, сможет выдержать тысячу кирпичей, но если отклонение на 5-ом кирпиче, столб никогда не станет выше трех десятков.
Архитектура, существовавшая с прототипа, была тем самым отклонением на 5-ом кирпиче, которая не позволяла добавить новый функционал, который нам так хотелось — тестирование в стиле BDD, в частности, использование Gherkin. Есть мнение, что нужно выбирать что-то одно. Либо TDD либо BDD. Поработав по принципам гибкого тестирования, я понял, что модульные и сценарные тесты ни в коем случае не должны противопоставляться, они прекрасное дополнение друг к другу!
Вот так выглядит матрица гибкого тестирования:
Модульные тесты относятся к квадранту 1 — низкоуровневые тесты. Предназначены для проектирования слабо-связанной, эластичной, тестируемой архитектуры. Они выполняют для разработчика ту же роль, что и страховочный трос для скалолаза. При этом они достаточно дешевые в разработке и содержании.
Сценарные тесты относятся к квадранту 2 — тесты более высокого уровня. Нужны, чтобы убедится, что мы правильно понимаем то, что нужно сделать. Это мостик между бизнесом и разработкой, возможность говорить на одном языке. Прекрасная документация, которая никогда не устареет. При этом они дороже в создании и более хрупкие.
По мне так прекрасная синергия. Я считаю, что для создания высококачественного ПО нужно использовать оба подхода вместе! Совмещенные тесты обоих видов создают отличную защиту от регрессии.
Немного забегу вперед и скажу, что на новом движке еще не реализовано тестирование в стиле BDD, но фундамент полностью подготовлен. Сценарное тестирование — это следующий шаг.
Архитектура
Она полностью переработана. Инструмент стал намного гибче и проще в разработке. Теперь это не обработка с 8,5к строк кода, теперь это ядро с системой плагинов. Да-да, тут нет ошибки, в 1С можно сделать фреймворк, который будет расширяться плагинами.
В качестве плагинов выступают внешние обработки, располагающиеся в папке “Plugins” и реализующие следующий базовый интерфейс:
// { Plugin interface
Функция ОписаниеПлагина(ВозможныеТипыПлагинов) Экспорт
Результат = Новый Структура;
Результат.Вставить("Тип", ВозможныеТипыПлагинов.Утилита);
Результат.Вставить("Идентификатор", Метаданные().Имя);
Результат.Вставить("Представление", Метаданные().Синоним);
Возврат Новый ФиксированнаяСтруктура(Результат);
КонецФункции
// } Plugin interface
Обращение к плагину происходит по идентификатору, например:
НекийПлагин = КонтекстЯдра.Плагин("НекийПлагин");
Работу ядра теперь можно грубо выразить в виде единственной функции:
КаноническийРезультатТестирования = ВыполнитьТесты(КаноническоеДеревоТестов);
Возникает резонный вопрос: “Что за канонические дерево тестов и результат тестирования?”.
Каноническое дерево тестов позволяет построить универсальную “запускалку” тестовых методов. Представляет собой древовидную структуру данных. Состоит из узлов следующих видов:
- контейнер — служит в целях группировки и может иметь подчиненные узлы. Так же для контейнера можно указать режим обработки дочерних узлов:
- случайный порядок обхода — нужен для модульных тестов, гарантирует независимость тестов друг от друга и позволяет находить всякие побочные эффекты;
- строгий порядок обхода — нужен для сценарных тестов, все дочерние узлы будут обрабатываться в строгом порядке. Кроме того, у контейнеров такого вида есть доп.свойство “контекст”, доступ к которому на чтение и запись имеют дочерние элементы.
- элемент — по сути метаданные тестового метода, которые позволяют создать контекст и вызвать тестовый метод. Имеет следующие основные свойства:
- Путь — строка, которая позволяет создать экземпляр объекта, у которого можно вызывать тестовый метод;
- ИмяМетода — собственно имя тестового метода, который будет выполняться;
- Параметры — массив параметров, передаваемых в тестовый метод. Количество и типы параметров должны соответствовать сигнатуре тестового метода. Необходим для параметризованных тестов.
Канонический результат тестирования очень похож на дерево тестов с тем отличием, что содержит дополнительную информацию о результатах выполнения:
- Для контейнеров:
- Состояние — результат агрегации состояний дочерних узлов;
- Статистика — общее количество тестов в контейнере, количество сломанных тестов, количество не реализованных тестов, общее время выполнения в мс.
- Для элементов:
- Состояние — не выполнялся / пройден / не реализован / сломан;
- Время выполнения;
- Сообщение — содержит текст исключения, если тест сломан.
Откуда берутся канонические деревья с тестами и как это способствует универсализации?
Построением деревьев с тестами занимается специальный тип плагинов “Загрузчик”. Типовой сценарий работы с загрузчиками:
- Пользователь выбирает Загрузчик, которым хочет воспользоваться;
- Далее определяется путь для поиска тестовых сценариев (возможно интерактивно). При этом путь — это просто некая строка, которую умеет верно интерпретировать выбранный загрузчик. Например, это может быть путь в файловой системе или путь в дереве метаданных конфигурации или … все что угодно;
- По выбранному пути производится поиск тестовых сценариев;
- По найденным сценариям формируется дерево тестов для ядра, при этом в путях у листьев дерева указываются некие строки (аналогично п.2), которые умеет верно интерпретировать выбранный загрузчик;
- Когда идет выполнение тестов, ядро для разрешения путей у листьев дерева обращается к соответствующему загрузчику.
API загрузчика:
Функция ВыбратьПутьИнтерактивно(ТекущийПуть = "") — клиентский метод для интерактивного выбора пути для загрузки тестов;
Функция Загрузить(КонтекстЯдра, Путь) — поиск тестовых сценариев и построение канонического дерева тестов;
Функция ПолучитьКонтекстПоПути(КонтекстЯдра, Путь) — получение тестового контекста по переданному пути.
На текущий момент реализовано 3 базовых загрузчика:
- ЗагрузчикФайлов — загрузчик модульных тестов из epf файлов. В качестве путей использует пути к файлам файловой системы;
- ЗагрузчикКаталогов — построен поверх загрузчика файлов. В качестве пути для поиска принимает путь к каталогу файловой системы. В остальном полностью полагается на загрузчика файлов;
- ЗагрузчикИзПодсистемКонфигурации — в качестве путей использует дерево метаданных конфигурации в строковом представлении. Например, “Метаданные.Обработки.Тест_Обработка”. API тестовых обработок заимствует загрузчика файлов.
В ближайших планах создание ЗагрузчикBDD, который будет работать с feature-файлами Gherkin.
А что делать с каноническими результатами тестирования?
После того как ядро переработало дерево тестов в результат тестирования, оно его передает еще одному специальному типу плагина “ГенераторОтчета”.
Генераторы отчетов трансформируют канонические результаты тестирования в любое другое представление. Например, сейчас реализованы:
- ГенераторОтчетаMXL — moxel-формат отчета, который сейчас используется при интерактивной работе с инструментом;
- ГенераторОтчетаJUnitXML — junit.xml отчет, который используется в непрерывной интеграции (CI).
API генератора отчета:
Функция СоздатьОтчет(КонтекстЯдра, РезультатыТестирования) — формирование отчета в формате предлагаемом плагином. Результаты тестирования передаются плагину в каноническом для ядра формате;
Процедура Показать(Отчет) — клиентский метод для интерактивного показа сформированного отчета. Плагин сам определяет, как именно нужно оформить для пользователя сформированный им отчет;
Процедура Экспортировать(Отчет, ПолныйПутьФайла) — сохраняет отчет по указанному пути, в основном используется для целей CI.
Полезняшки
Всякие “полезняшки” имеют тип плагина “Утилита”. Как правило, выполняют библиотечную или сервисную функцию. Например, библиотека утверждений в стиле BDD — плагин “УтвержденияBDD”, о котором я писал в своей прошлой статье. “СериализаторMXL” — сервисный плагин, который занимается сериализацией данных базы в moxel-формат и обратно.
Послесловие
До влития в основной ствол разработки, исходники доступны по ссылке.
Теперь можно утверждать, что фреймворк вырос из “просто инструмент для модульного тестирования” в “инструмент позволяющий покрыть почти все возможные виды автоматического тестирования”. Он модульный, четко разделен по слоям, легко может дорабатываться и расширяться. Кажется, текущее название перестало передавать назначение продукта. Может, пришло время изменить название? Если у вас есть хорошая идея для названия, не стесняйтесь поделиться.
CrackedSapphire
Примеры запилите, пожалуйста.
wizi4d
Фреймворк тестируется сам на себе, поэтому в исходниках все есть. Например:
API файлового загрузчика нового вида
API файлового загрузчика старого вида