mirror of
https://github.com/bia-technologies/yaxunit.git
synced 2025-04-20 11:38:01 +02:00
99 lines
10 KiB
Markdown
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
|
|
* не захламлять контекстную подсказку свойствами обработки (или формы для клиента)
|
|
* оптимизировать время работы (нет затрат на создание обработок)
|
|
|
|
Дополнительно, так как контекст глобальный, не обязательно передавать объект, чтобы получить доступ к настроенному контексту.
|
|
Например:
|
|
|
|
* При регистрации теста, информация о тестах никуда не возвращается, движок, после вызова метода `ИсполняемыеСценарии`, просто считывает результат из глобального контекста.
|
|
* Или настройки мокито, выполненные в методе теста сразу же доступны в методах других модулей (перехватываемых).
|