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

238 lines
21 KiB
Markdown
Raw Normal View History

2024-03-28 21:18:51 +03:00
---
tags: [Начало, Предикаты, Утверждения, Запросы, Мокирование]
sidebar_position: 4
---
# Предикаты
Предикаты это довольно мощный и универсальный инструмент. С помощью предикатов вы формируете набор условий, сродни отбору. Который можно использовать:
1. В утверждениях для [проверки коллекций](assertions/assertions-base.md#проверка-на-соответствие-набору-условий-предикату)
2. В утверждениях для [проверки записей базы](assertions/assertions-db)
3. Для [получения данных базы](auxiliary-modules/queries.md)
2025-02-07 03:32:08 +03:00
4. Для указания условий при [обучении Мокито](mocking/mockito/mockito.md#параметры)
2024-03-28 21:18:51 +03:00
Предикаты расширяют и унифицируют функциональность тестового движка.
Механизм предикатов ([ЮТест.Предикат](/api/ЮТест#предикат)):
* позволяет формировать наборы условий (отборы) и передавать их в качества параметров;
* построен по модели [текучих выражений](/docs/getting-started/fluent-api.md) и имеет схожий с базовыми утверждениями синтаксис ([`ЮТест.ОжидаетЧто()`](/api/ЮТУтверждения));
* позволяет упростить и унифицировать многие механизмы движка, некоторые еще только в планах;
* за счет этого, расширение функциональности предикатов автоматические расширяет функциональность многих механизмов движка.
Чтобы воспользоваться предикатами, вам нужно сначала создать их с помощью конструктора [ЮТест.Предикат](/api/ЮТест#предикат), а затем передать в метод.
Например, нам нужно проверить формирование записей в регистре.
```bsl
Процедура АктуализацияУведомлений() Экспорт
// Тест удостовериться в отсутствии нужных записей перед вызовом метода
// Вызовет метод формирующий записи в регистре
// Проверит наличие сформированных записей
// А также проверит записи на соответствие требований
ИмяРегистра = "РегистрСведений.ОповещенияПользователя";
Объект = ТестовыеДанные.Объект();
// Для этого мы формируем отбор поиска записей
Отбор = ЮТест.Предикат()
.Реквизит("Источник").Равно(Объект)
.Реквизит("ТипОповещения").Равно(Справочники.ТипыОповещенийПользователя.Уведомление)
.Получить();
// По этому отбору проверим отсутствие нужных записей
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.НеСодержитЗаписи(Отбор);
УведомленияВызовСервера.АктуализацияУведомлений();
// А после вызова метода - присутствие
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.СодержитЗаписи(Отбор);
// Также получим сами записи используя тот же отбор
ДанныеУведомления = ЮТЗапросы.Запись(ИмяРегистра, Отбор);
ЮТест.ОжидаетЧто(ДанныеУведомления)
.Свойство("Прочитано").ЭтоЛожь()
.Свойство("Пользователь").Равно(Справочники.ГруппыОповещенийПользователей.Инженер);
КонецПроцедуры
```
## Возможности
* Проверка вложенных свойств:
* [`Реквизит`](/api/ЮТПредикаты#реквизит) - Устанавливает имя реквизита, который необходимо проверить. Все последующие проверки будут относится к нему.
* [`Свойство`](/api/ЮТПредикаты#свойство) - Это алиас (псевдоним) для `Реквизит`
* Проверки
* [`Равно`](/api/ЮТПредикаты#равно) - Добавляет предикат, проверяющий равенство объекта (свойства) указанному значению
* [`НеРавно`](/api/ЮТПредикаты#неравно) - Добавляет предикат, проверяющий не равенство объекта (свойства) указанному значению
* [`Заполнено`](/api/ЮТПредикаты#заполнено) - Добавляет предикат, проверяющий заполненность объекта (свойства)
* [`Пусто`](/api/ЮТПредикаты#пусто) - Добавляет предикат, проверяющий, что объект (свойств) не заполнено
* [`Больше`](/api/ЮТПредикаты#больше) - Добавляет предикат, проверяющий, что значение объекта (свойства) больше указанного
* [`БольшеИлиРавно`](/api/ЮТПредикаты#большеилиравно) - Добавляет предикат, проверяющий, что значение объекта (свойства) больше или равно указанному
* [`Меньше`](/api/ЮТПредикаты#меньше) - Добавляет предикат, проверяющий, что значение объекта (свойства) меньше указанного
* [`МеньшеИлиРавно`](/api/ЮТПредикаты#меньшеилиравно) - Добавляет предикат, проверяющий, что значение объекта (свойства) меньше или равно указанному
* [`ИмеетТип`](/api/ЮТПредикаты#имееттип) - Добавляет предикат, проверяющий, что значение объекта (свойства) имеет указанный тип
* [`ИмеетТипОтличныйОт`](/api/ЮТПредикаты#имееттипотличныйот) - Добавляет предикат, проверяющий, что значение объекта (свойства) имеет тип отличный от указанного
* [`ИмеетДлину`](/api/ЮТПредикаты#имеетдлину) - Добавляет предикат, проверяющий, длину/размер значение объекта (свойства) на равенство указанному значению
* [`ИмеетДлинуОтличнуюОт`](/api/ЮТПредикаты#имеетдлинуотличнуюот) - Добавляет предикат, проверяющий, длину/размер значение объекта (свойства) на не равенство указанному значению
* [`ИмеетСвойство`](/api/ЮТПредикаты#имеетсвойство) - Добавляет предикат, проверяющий, что значение объекта (реквизита) содержит вложенное свойство
* [`НеИмеетСвойства`](/api/ЮТПредикаты#неимеетсвойства) - Добавляет предикат, проверяющий, что значение объекта (реквизита) не содержит вложенное свойство
* [`Содержит`](/api/ЮТПредикаты#содержит) - Добавляет предикат, проверяющий, что значение объекта (реквизита) содержит указанное значение
* [`НеСодержит`](/api/ЮТПредикаты#несодержит) - Добавляет предикат, проверяющий, что значение объекта (реквизита) не содержит указанное значение
* [`СодержитСтрокуПоШаблону`](/api/ЮТПредикаты#содержитстрокупошаблону) - Добавляет предикат, проверяющий, что строка соответствует указанному регулярному выражению
* [`НеСодержитСтрокуПоШаблону`](/api/ЮТПредикаты#несодержитстрокупошаблону) - Добавляет предикат, проверяющий, что строка не соответствует указанному регулярному выражению
* [`ВСписке`](/api/ЮТПредикаты#всписке) - Добавляет условие, что проверяемое значение (или значение его свойства) входит в список значений
* Между
* [`Между`](/api/ЮТПредикаты#между)/[`МеждуВключаяГраницы`](/api/ЮТПредикаты#междувключаяграницы)- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
Проверяемое значение **может** находится на границе интервала.
* [`МеждуИсключаяГраницы`](/api/ЮТПредикаты#междуисключаяграницы)- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
Проверяемое значение **не может** находится на границе интервала.
* [`МеждуВключаяНачалоГраницы`](/api/ЮТПредикаты#междувключаяначалограницы)- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
Проверяемое значение **может** находится на **начальной** границе интервала.
* [`МеждуВключаяОкончаниеГраницы`](/api/ЮТПредикаты#междувключаяокончаниеграницы)- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
Проверяемое значение **может** находится на **конечной** границе интервала.
2024-03-28 21:18:51 +03:00
* Служебные
* [`Получить`](/api/ЮТПредикаты#получить) - Возвращает набор сформированных утверждений.
Рекомендуется использовать этот метод, если планируется отложенная проверка предикатов. Например, вы хотите сформировать два набору предикатов и проверять их в зависимости от условия.
Метод копирует настроенный набор утверждений в массив и возвращает его, таким образом сохраняется состояние, которое можно передавать дальше.
Возможно создавать предикаты на основании структуры - `ЮТест.Предикат(Структура)`, например:
```bsl
// Вместо
Предикат = ЮТест.Предикат()
.Свойство("Наименование").Равно(НаименованиеОбъекта)
.Свойство("Код").Равно(КодОбъекта);
// Можно использовать структур
Условия = Новый Структура("Наименование, Код", НаименованиеОбъекта, КодОбъекта);
Предикат = ЮТест.Предикат(Условия);
```
## Примеры использования
* Проверка коллекции
```bsl
// Проверят, что в коллекции есть элементы с реквизитом `Число`, значение которого равно `2`
ЮТест.ОжидаетЧто(Коллекция)
.ЛюбойЭлементСоответствуетПредикату(ЮТест.Предикат()
.Реквизит("Число").Равно(2));
// Тоже самое, что и проверка выше
ЮТест.ОжидаетЧто(Коллекция)
.Содержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2));
// Проверят, что каждый элемент коллекции это заполненный массив
ЮТест.ОжидаетЧто(Коллекция)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.Заполнено().ИмеетТип("Массив"));
// Проверят, что в коллекции нет элементов с реквизитом `Число`, значение которого равно `2`
ЮТест.ОжидаетЧто(Коллекция)
.НеСодержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2));
```
* Описания параметров метода при мокировании
Например, имеем метод, который принимает в параметрах структуру. Необходимо вернуть 2 разных результата в зависимости от значения реквизита входной структуры.
```bsl title="Проверяемый метод"
Функция Посчитать(Параметры)
Если Параметры.Оператор = "Сложить" Тогда
Возврат Параметры.Операнд1 + Параметры.Операнд2;
ИначеЕсли Параметры.Оператор = "Вычесть" Тогда
Возврат Параметры.Операнд1 - Параметры.Операнд2;
КонецЕсли;
КонецФункции
```
```bsl title="Тест"
Мокито.Обучение(Модуль)
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Сложить")))
.ВернутьРезультат(Результат1)
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Вычесть")))
.ВернутьРезультат(Результат2);
```
* Утверждения, проверяющие данные в базе на основании предикатов.
```bsl
ЮТест.ОжидаетЧтоТаблица("Справочник.Товары").СодержитЗаписи(
ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение)
);
```
* Получение записей из базы
```bsl
ДанныеТовара = ЮТЗапросы.Запись("Справочник.Товары", ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение));
```
## Особенности
### Особенности контекста
Предикаты как и большинство механизмов построены на [текучих выражениях](/docs/getting-started/fluent-api.md) с сохранением состояния в глобальном [контексте](context.md).
Это приводит к тому, что вы не можете сразу использовать несколько предикатов, например
```bsl
Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка"),
ЮТест.Предикат().ИмеетТип("Число")))
.ВернутьРезультат(Результат1);
```
В этом примере 1С сначала вычислит выражения для всех параметров, а потом передаст их в метод и мы получим для обоих параметров один и тот же предикат, ожидающий тип `Число`.
А все потому, что методы настройки предиката возвращают общий модуль-конструктор. Таким образом оба параметра будут иметь одно и тоже значение - общий модуль `ЮТПредикаты`, который вернет одну и туже настройку (из глобального контекста).
Можно переписать настройку мокито, для большей наглядности, с использованием переменных:
```bsl
Параметр1 = ЮТест.Предикат().ИмеетТип("Строка"); // Параметр1 = ОбщийМодуль.ЮТПредикаты
Параметр2 = ЮТест.Предикат().ИмеетТип("Число"); // Параметр2 = ОбщийМодуль.ЮТПредикаты
ЮТест.ОжидаетЧто(Параметр1).Равно(Параметр2); // Это утверждение будет успешным
Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(Параметр1, Параметр2))
.ВернутьРезультат(Результат1);
```
Для обхода этой проблемы можно использовать метод `Получить`, который возвращает текущее состояние (настройки) из конструктора и передает его в параметр метода.
```bsl
Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка").Получить(),
ЮТест.Предикат().ИмеетТип("Число")))
.ВернутьРезультат(Результат1);
```
Такая же история при сохранение предикатов в переменные.
```bsl
ПроверкаСтрока = ЮТест.Предикат().ИмеетТип("Строка");
ПроверкаЧисло = ЮТест.Предикат().ИмеетТип("Число");
```
`ПроверкаСтрока` и `ПроверкаЧисло` будут равны и содержать одинаковые условия. Проблему также можно обойти используя метод `Получить`.
```bsl
ПроверкаСтрока = ЮТест.Предикат().ИмеетТип("Строка").Получить();
ПроверкаЧисло = ЮТест.Предикат().ИмеетТип("Число").Получить();
```
### Особенности реализации
Сам модуль предикатов используется только для формирования набора утверждений/условий.
За применение их в разных механизмах, реализацией проверок и формированием условий, отвечают другие модули и возможна ситуация, когда некоторые предикаты еще не реализованы или не поддерживаются каким-либо механизмом. Например, проверка заполненности не поддерживается запросами.