Текучий интерфейс
Большая часть публичной функциональности тестового движка YAxUnit реализована с использование текучих выражений (Fluent interface)
Основная цель их использования - улучшение читаемости кода и упрощение работы с движком. Текучие выражения хорошо зарекомендовали себя и широко применяются в других языках (примеры: C#: builder pattern, java: stream-api, php: dsl, C#: tests assertions, 1С: текучие утверждения, 1С: Элемент, 1С: объектная модель запроса). Текучие выражения - это цепочка методов, объединенных одним контекстом, который они настраивают/обрабатывают. Он упрощает множественные вызовы методов одного и того же объекта. Например:
Документ = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара)
.Установить("Поставщик", Поставщик) // Устанавливает значение реквизита документа.
.Фикция("Дата") // Генерирует случайную дату.
.Фикция("Номер") // Генерирует случайный номер.
.ФикцияРеквизитов("Склад, Валюта") // Создает "пустышки" склада и валюты.
.ТабличнаяЧасть("Товары") // Переключает контекст на работу с табличной частью "Товары".
.ДобавитьСтроку() // Добавляет строку табличной части.
.Фикция("Товар", Новый Структура("Поставщик", Поставщик)) // Создает "пустышку" товары от конкретного поставщика.
.Фикция("Цена") // Генерирует случайную цену.
.Провести();
ЮТест.ОжидаетЧто(Документ)
.Свойство("Дата").Заполнено() // Проверяет, что дата документа заполнена.
.Свойство("Номер").Заполнено() // Проверяет, что номер документа заполнен.
.Свойство("Склад").Заполнено() // Проверяет, что склад документа заполнен.
.Свойство("Валюта").Заполнено() // Проверяет, что валюта документа заполнена.
.Свойство("Товары").ИмеетДлину(1) // Проверяет, что табличная часть "Товары" имеет длину 1.
.Свойство("Товары[0].Товар").Заполнено() // Проверяет, что товар из первой строки заполнен
.Свойство("Товары[0].Товар.Поставщик").Равно(Поставщик); // и имеет поставщика, равного нужному
Примеры выше можно было бы реализовать без использования текучих выражений, например:
Документ = Документы.ПриходТовара.СоздатьДокумент();
Документ.Поставщик = Поставщик;
Документ.Дата = ЮТест.Данные().СлучайнаяДата();
Документ.Номер = ЮТест.Данные().СлучайнаяСтрока();
Документ.Склад = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Склад"));
Документ.Валюта = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Валюты"));
СтрокаТовары = Документ.Товары.Добавить();
СтрокаТовары.Товар = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Товары"), Новый Структура("Поставщик", Поставщик));
СтрокаТовары.Цена = ЮТест.Данные().СлучайноеПоложительноеЧисло();
Документ.Записать(РежимЗаписиДокумента.Проведение);
Ссылка = Документ.Ссылка;
К онструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара);
Конструктор.Установить("Поставщик", Поставщик);
Конструктор.ФикцияРеквизитов("Дата, Ном ер, Склад, Валюта");
Конструктор.ТабличнаяЧасть("Товары");
Конструктор.ДобавитьСтроку();
Конструктор.Фикция("Товар", Новый Структура("Поставщи к", Поставщик));
Конструктор.Фикция("Цена");
Документ = Конструктор.Провести();
Первый вариант (с использованием текучих выражений) не содержит "служебного" кода (обращения к документу или конструктору), таким образом концентрация полезного кода выше, меньше отвлекающих факторов и больше акцент на то, что действительно важно.
Но при этом текучие выражения имеют и ряд минусов:
- Проблема с отладкой, нельзя поставить точку внутри цепочки, только проход по шагам.
- Не работает контекстная подсказка в конфигураторе (хотя она и так редко работает).
- Необходимость привычки и использовать "правильное" форматирование (отступы).
Принцип работы текучих выражений в YAxUnit
Текучие выражения работают за счет передачи контекста между вызовами, достигается это тем, что объект, реализующий интерфейс текучих выражений:
- хранит внутри себя контекст(состояние)
- возвращает сам себя из методов
- или новый объект, передав в него текущий контекст.
В языках, поддерживающих ООП, под каждый текучий интерфейс создается свой класс, хранящий контекст и предоставляющий необходимый API. В 1С это можно реализовать в виде набора обработок. Таким образом в YAxUnit созданы:
- Конструктор объектов информационной базы,
ЮТест.Данные().КонструкторОбъекта
. - Конструктор объектов XDTO,
ЮТест.Данные().КонструкторОбъектаXDTO
. - Эмулятор выборки данных ADO.RecordSet,
ЮТест.Данные().ADORecordSet
. - Эмулятор запроса к http сервису,
ЮТест.Данные().HTTPСервисЗапрос
.
А есть, другая часть API построенная на текучих выражения, она реализуется через общие модули и хранение состояния в глобальном контексте.
Общие модули позволяют, в отличии от обработок:
- создавать API доступный и на клиенте, и на сервере
- избежать дублирования кода, который приводи к расхождению логики api
- не захламлять контекстную подсказку свойствами обработки (или формы для клиента)
- оптимизировать время работы (нет затрат на создание обработок)
Дополнительно, так как контекст глобальный, не обязательно передавать объект, чтобы получить доступ к настроенному контексту.
Например:
- При регистрации теста, информация о тестах никуда не возвращается, движок, после вызова метода
ИсполняемыеСценарии
, просто считывает результат из глобального контекста. - Или настройки мокито, выполненные в методе теста сразу же доступны в методах других модулей (перехватываемых).