До релиза новой версии фреймворка по тестированию “xUnitFor1C” осталось совсем немного, а значит пришло время рассказать о проделанной работе и о том, что ожидает пользователей.

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

Зачем все перепиливать?


Насколько я понимаю, на момент, когда проект только рождался, основной целью было понять, насколько модульное тестирование может быть востребовано в среде 1С. Понятное дело, что продумывать и выделять уровни абстракций на этапе прототипирования — дело не особо перспективное. Прототип не обладает эластичностью настоящего кода. Прототип — это эксперимент, результаты которого нужно выбрасывать.

Далее флаг разработки переходил от одного энтузиаста к другому, при этом базовая архитектура оставалась прежней. С ростом популярности продукта и осознания того, что хотелось бы получить, стало все сложнее вносить изменения.

Мне нравится метафора Алана Купера:
Создание большой программы можно сравнить с постройкой столба из кирпича. Этот столб состоит из тысячи кирпичей, положенных один на другой. Столб может быть выстроен, только если класть кирпичи с большой точностью. Любое отклонение приведет к падению кирпичей. Если кирпич с номером 998 сможет отклонить на пять миллиметров, столб, вероятно, сможет выдержать тысячу кирпичей, но если отклонение на 5-ом кирпиче, столб никогда не станет выше трех десятков.


Архитектура, существовавшая с прототипа, была тем самым отклонением на 5-ом кирпиче, которая не позволяла добавить новый функционал, который нам так хотелось — тестирование в стиле BDD, в частности, использование Gherkin. Есть мнение, что нужно выбирать что-то одно. Либо TDD либо BDD. Поработав по принципам гибкого тестирования, я понял, что модульные и сценарные тесты ни в коем случае не должны противопоставляться, они прекрасное дополнение друг к другу!

Вот так выглядит матрица гибкого тестирования:

image

Модульные тесты относятся к квадранту 1 — низкоуровневые тесты. Предназначены для проектирования слабо-связанной, эластичной, тестируемой архитектуры. Они выполняют для разработчика ту же роль, что и страховочный трос для скалолаза. При этом они достаточно дешевые в разработке и содержании.

Сценарные тесты относятся к квадранту 2 — тесты более высокого уровня. Нужны, чтобы убедится, что мы правильно понимаем то, что нужно сделать. Это мостик между бизнесом и разработкой, возможность говорить на одном языке. Прекрасная документация, которая никогда не устареет. При этом они дороже в создании и более хрупкие.

По мне так прекрасная синергия. Я считаю, что для создания высококачественного ПО нужно использовать оба подхода вместе! Совмещенные тесты обоих видов создают отличную защиту от регрессии.

Немного забегу вперед и скажу, что на новом движке еще не реализовано тестирование в стиле BDD, но фундамент полностью подготовлен. Сценарное тестирование — это следующий шаг.

Архитектура


Она полностью переработана. Инструмент стал намного гибче и проще в разработке. Теперь это не обработка с 8,5к строк кода, теперь это ядро с системой плагинов. Да-да, тут нет ошибки, в 1С можно сделать фреймворк, который будет расширяться плагинами.

В качестве плагинов выступают внешние обработки, располагающиеся в папке “Plugins” и реализующие следующий базовый интерфейс:
// { Plugin interface
Функция ОписаниеПлагина(ВозможныеТипыПлагинов) Экспорт
	Результат = Новый Структура;
	Результат.Вставить("Тип", ВозможныеТипыПлагинов.Утилита);
	Результат.Вставить("Идентификатор", Метаданные().Имя);
	Результат.Вставить("Представление", Метаданные().Синоним);
	
	Возврат Новый ФиксированнаяСтруктура(Результат);
КонецФункции
// } Plugin interface

Обращение к плагину происходит по идентификатору, например:
НекийПлагин = КонтекстЯдра.Плагин("НекийПлагин");

Работу ядра теперь можно грубо выразить в виде единственной функции:
КаноническийРезультатТестирования = ВыполнитьТесты(КаноническоеДеревоТестов);
Возникает резонный вопрос: “Что за канонические дерево тестов и результат тестирования?”.

image

Каноническое дерево тестов позволяет построить универсальную “запускалку” тестовых методов. Представляет собой древовидную структуру данных. Состоит из узлов следующих видов:
  • контейнер — служит в целях группировки и может иметь подчиненные узлы. Так же для контейнера можно указать режим обработки дочерних узлов:
    • случайный порядок обхода — нужен для модульных тестов, гарантирует независимость тестов друг от друга и позволяет находить всякие побочные эффекты;
    • строгий порядок обхода — нужен для сценарных тестов, все дочерние узлы будут обрабатываться в строгом порядке. Кроме того, у контейнеров такого вида есть доп.свойство “контекст”, доступ к которому на чтение и запись имеют дочерние элементы.

  • элемент — по сути метаданные тестового метода, которые позволяют создать контекст и вызвать тестовый метод. Имеет следующие основные свойства:
    • Путь — строка, которая позволяет создать экземпляр объекта, у которого можно вызывать тестовый метод;
    • ИмяМетода — собственно имя тестового метода, который будет выполняться;
    • Параметры — массив параметров, передаваемых в тестовый метод. Количество и типы параметров должны соответствовать сигнатуре тестового метода. Необходим для параметризованных тестов.


Канонический результат тестирования очень похож на дерево тестов с тем отличием, что содержит дополнительную информацию о результатах выполнения:
  • Для контейнеров:
    • Состояние — результат агрегации состояний дочерних узлов;
    • Статистика — общее количество тестов в контейнере, количество сломанных тестов, количество не реализованных тестов, общее время выполнения в мс.

  • Для элементов:
    • Состояние — не выполнялся / пройден / не реализован / сломан;
    • Время выполнения;
    • Сообщение — содержит текст исключения, если тест сломан.


Откуда берутся канонические деревья с тестами и как это способствует универсализации?


Построением деревьев с тестами занимается специальный тип плагинов “Загрузчик”. Типовой сценарий работы с загрузчиками:
  1. Пользователь выбирает Загрузчик, которым хочет воспользоваться;
  2. Далее определяется путь для поиска тестовых сценариев (возможно интерактивно). При этом путь — это просто некая строка, которую умеет верно интерпретировать выбранный загрузчик. Например, это может быть путь в файловой системе или путь в дереве метаданных конфигурации или … все что угодно;
  3. По выбранному пути производится поиск тестовых сценариев;
  4. По найденным сценариям формируется дерево тестов для ядра, при этом в путях у листьев дерева указываются некие строки (аналогично п.2), которые умеет верно интерпретировать выбранный загрузчик;
  5. Когда идет выполнение тестов, ядро для разрешения путей у листьев дерева обращается к соответствующему загрузчику.

API загрузчика:

Функция ВыбратьПутьИнтерактивно(ТекущийПуть = "") — клиентский метод для интерактивного выбора пути для загрузки тестов;

Функция Загрузить(КонтекстЯдра, Путь) — поиск тестовых сценариев и построение канонического дерева тестов;

Функция ПолучитьКонтекстПоПути(КонтекстЯдра, Путь) — получение тестового контекста по переданному пути.

На текущий момент реализовано 3 базовых загрузчика:
  • ЗагрузчикФайлов — загрузчик модульных тестов из epf файлов. В качестве путей использует пути к файлам файловой системы;
  • ЗагрузчикКаталогов — построен поверх загрузчика файлов. В качестве пути для поиска принимает путь к каталогу файловой системы. В остальном полностью полагается на загрузчика файлов;
  • ЗагрузчикИзПодсистемКонфигурации — в качестве путей использует дерево метаданных конфигурации в строковом представлении. Например, “Метаданные.Обработки.Тест_Обработка”. API тестовых обработок заимствует загрузчика файлов.

В ближайших планах создание ЗагрузчикBDD, который будет работать с feature-файлами Gherkin.

А что делать с каноническими результатами тестирования?


После того как ядро переработало дерево тестов в результат тестирования, оно его передает еще одному специальному типу плагина “ГенераторОтчета”.

Генераторы отчетов трансформируют канонические результаты тестирования в любое другое представление. Например, сейчас реализованы:
  • ГенераторОтчетаMXL — moxel-формат отчета, который сейчас используется при интерактивной работе с инструментом;
  • ГенераторОтчетаJUnitXML — junit.xml отчет, который используется в непрерывной интеграции (CI).

API генератора отчета:

Функция СоздатьОтчет(КонтекстЯдра, РезультатыТестирования) — формирование отчета в формате предлагаемом плагином. Результаты тестирования передаются плагину в каноническом для ядра формате;

Процедура Показать(Отчет) — клиентский метод для интерактивного показа сформированного отчета. Плагин сам определяет, как именно нужно оформить для пользователя сформированный им отчет;

Процедура Экспортировать(Отчет, ПолныйПутьФайла) — сохраняет отчет по указанному пути, в основном используется для целей CI.

Полезняшки


Всякие “полезняшки” имеют тип плагина “Утилита”. Как правило, выполняют библиотечную или сервисную функцию. Например, библиотека утверждений в стиле BDD — плагин “УтвержденияBDD”, о котором я писал в своей прошлой статье. “СериализаторMXL” — сервисный плагин, который занимается сериализацией данных базы в moxel-формат и обратно.

Послесловие


До влития в основной ствол разработки, исходники доступны по ссылке.

Теперь можно утверждать, что фреймворк вырос из “просто инструмент для модульного тестирования” в “инструмент позволяющий покрыть почти все возможные виды автоматического тестирования”. Он модульный, четко разделен по слоям, легко может дорабатываться и расширяться. Кажется, текущее название перестало передавать назначение продукта. Может, пришло время изменить название? Если у вас есть хорошая идея для названия, не стесняйтесь поделиться.

Комментарии (3)


  1. CrackedSapphire
    04.11.2015 00:42

    Примеры запилите, пожалуйста.


    1. wizi4d
      04.11.2015 18:31

      Фреймворк тестируется сам на себе, поэтому в исходниках все есть. Например:
      API файлового загрузчика нового вида
      API файлового загрузчика старого вида


  1. vaniaPooh
    04.11.2015 15:16

    Allure прикрутите, пожалуйста. Вроде бы такой PR уже даже есть.