1
0
mirror of https://github.com/bia-technologies/yaxunit.git synced 2025-04-20 11:38:01 +02:00

99 lines
10 KiB
Markdown

# Текучий интерфейс
Большая часть публичной функциональности тестового движка YAxUnit реализована с использование [`текучих выражений (Fluent interface)`](https://ru.wikipedia.org/wiki/Fluent_interface)
Основная цель их использования - улучшение читаемости кода и упрощение работы с движком. Текучие выражения хорошо зарекомендовали себя и широко применяются в других языках (примеры: [C#: builder pattern](https://metanit.com/sharp/patterns/6.1.php), [java: stream-api](https://javarush.com/groups/posts/2203-stream-api), [php: dsl](https://ru.hexlet.io/courses/php-object-oriented-design/lessons/fluent-interface/theory_unit), [C#: tests assertions](https://fluentassertions.com/introduction), [1С: текучие утверждения](https://habr.com/ru/articles/260013/), [1С: Элемент](https://its.1c.ru/db/pubelementlang/content/18/hdoc), [1С: объектная модель запроса](https://infostart.ru/1c/articles/1991009/)).
Текучие выражения - это цепочка методов, объединенных одним контекстом, который они настраивают/обрабатывают. Он упрощает множественные вызовы методов одного и того же объекта.
Например:
```bsl title="Создание документа с использованием текучих выражений"
Документ = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара)
.Установить("Поставщик", Поставщик) // Устанавливает значение реквизита документа.
.Фикция("Дата") // Генерирует случайную дату.
.Фикция("Номер") // Генерирует случайный номер.
.ФикцияРеквизитов("Склад, Валюта") // Создает "пустышки" склада и валюты.
.ТабличнаяЧасть("Товары") // Переключает контекст на работу с табличной частью "Товары".
.ДобавитьСтроку() // Добавляет строку табличной части.
.Фикция("Товар", Новый Структура("Поставщик", Поставщик)) // Создает "пустышку" товары от конкретного поставщика.
.Фикция("Цена") // Генерирует случайную цену.
.Провести();
```
```bsl title="Проверка документа с использованием текучих выражений"
ЮТест.ОжидаетЧто(Документ)
.Свойство("Дата").Заполнено() // Проверяет, что дата документа заполнена.
.Свойство("Номер").Заполнено() // Проверяет, что номер документа заполнен.
.Свойство("Склад").Заполнено() // Проверяет, что склад документа заполнен.
.Свойство("Валюта").Заполнено() // Проверяет, что валюта документа заполнена.
.Свойство("Товары").ИмеетДлину(1) // Проверяет, что табличная часть "Товары" имеет длину 1.
.Свойство("Товары[0].Товар").Заполнено() // Проверяет, что товар из первой строки заполнен
.Свойство("Товары[0].Товар.Поставщик").Равно(Поставщик); // и имеет поставщика, равного нужному
```
Примеры выше можно было бы реализовать без использования текучих выражений, например:
```bsl title="Создание документа с заполнением случайных значений"
Документ = Документы.ПриходТовара.СоздатьДокумент();
Документ.Поставщик = Поставщик;
Документ.Дата = ЮТест.Данные().СлучайнаяДата();
Документ.Номер = ЮТест.Данные().СлучайнаяСтрока();
Документ.Склад = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Склад"));
Документ.Валюта = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Валюты"));
СтрокаТовары = Документ.Товары.Добавить();
СтрокаТовары.Товар = ЮТест.Данные().Фикция(Тип("СправочникСсылка.Товары"), Новый Структура("Поставщик", Поставщик));
СтрокаТовары.Цена = ЮТест.Данные().СлучайноеПоложительноеЧисло();
Документ.Записать(РежимЗаписиДокумента.Проведение);
Ссылка = Документ.Ссылка;
```
```bsl title="Или вариант используя конструктора, но без цепочки вызовов"
Конструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара);
Конструктор.Установить("Поставщик", Поставщик);
Конструктор.ФикцияРеквизитов("Дата, Номер, Склад, Валюта");
Конструктор.ТабличнаяЧасть("Товары");
Конструктор.ДобавитьСтроку();
Конструктор.Фикция("Товар", Новый Структура("Поставщик", Поставщик));
Конструктор.Фикция("Цена");
Документ = Конструктор.Провести();
```
Первый вариант (с использованием текучих выражений) не содержит "служебного" кода (обращения к документу или конструктору), таким образом концентрация полезного кода выше, меньше отвлекающих факторов и больше акцент на то, что действительно важно.
Но при этом текучие выражения имеют и ряд минусов:
* Проблема с отладкой, нельзя поставить точку внутри цепочки, только проход по шагам.
* Не работает контекстная подсказка в конфигураторе (хотя она и так редко работает).
* Необходимость привычки и использовать "правильное" форматирование (отступы).
## Принцип работы текучих выражений в YAxUnit
Текучие выражения работают за счет передачи контекста между вызовами, достигается это тем, что объект, реализующий интерфейс текучих выражений:
* хранит внутри себя контекст(состояние)
* возвращает сам себя из методов
* или новый объект, передав в него текущий контекст.
В языках, поддерживающих ООП, под каждый текучий интерфейс создается свой класс, хранящий контекст и предоставляющий необходимый API. В 1С это можно реализовать в виде набора обработок.
Таким образом в YAxUnit созданы:
* Конструктор объектов информационной базы, `ЮТест.Данные().КонструкторОбъекта`.
* Конструктор объектов XDTO, `ЮТест.Данные().КонструкторОбъектаXDTO`.
* Эмулятор выборки данных ADO.RecordSet, `ЮТест.Данные().ADORecordSet`.
* Эмулятор запроса к http сервису, `ЮТест.Данные().HTTPСервисЗапрос`.
А есть, другая часть API построенная на текучих выражения, она реализуется через общие модули и хранение состояния в глобальном контексте.
Общие модули позволяют, в отличии от обработок:
* создавать API доступный и на клиенте, и на сервере
* избежать дублирования кода, который приводи к расхождению логики api
* не захламлять контекстную подсказку свойствами обработки (или формы для клиента)
* оптимизировать время работы (нет затрат на создание обработок)
Дополнительно, так как контекст глобальный, не обязательно передавать объект, чтобы получить доступ к настроенному контексту.
Например:
* При регистрации теста, информация о тестах никуда не возвращается, движок, после вызова метода `ИсполняемыеСценарии`, просто считывает результат из глобального контекста.
* Или настройки мокито, выполненные в методе теста сразу же доступны в методах других модулей (перехватываемых).