Перейти к основному содержимому

Предикаты

Предикаты — это декларативный способ описания условий для проверки объектов, коллекций и данных в базе. Они позволяют формировать сложные проверки с помощью цепочек методов (fluent-интерфейс), что делает тесты более читаемыми и поддерживаемыми.

Предикаты это довольно мощный и универсальный инструмент. С помощью предикатов вы формируете набор условий, сродни отбору. Который можно использовать:

  1. В утверждениях для проверки коллекций
  2. В утверждениях для проверки записей базы
  3. Для получения данных базы
  4. Для указания условий при обучении Мокито

Предикаты расширяют и унифицируют функциональность тестового движка.

Механизм предикатов (ЮТест.Предикат):

  • позволяет формировать наборы условий (отборы) и передавать их в качества параметров;
  • построен по модели текучих выражений и имеет схожий с базовыми утверждениями синтаксис (ЮТест.ОжидаетЧто());
  • позволяет упростить и унифицировать многие механизмы движка, некоторые еще только в планах;
  • за счет этого, расширение функциональности предикатов автоматические расширяет функциональность многих механизмов движка.

Чтобы воспользоваться предикатами, вам нужно сначала создать их с помощью конструктора ЮТест.Предикат, а затем передать в метод.

Например, нам нужно проверить формирование записей в регистре.

Процедура АктуализацияУведомлений() Экспорт

// Тест удостовериться в отсутствии нужных записей перед вызовом метода
// Вызовет метод формирующий записи в регистре
// Проверит наличие сформированных записей
// А также проверит записи на соответствие требований

ИмяРегистра = "РегистрСведений.ОповещенияПользователя";
Объект = ТестовыеДанные.Объект();

// Для этого мы формируем отбор поиска записей
Отбор = ЮТест.Предикат()
.Реквизит("Источник").Равно(Объект)
.Реквизит("ТипОповещения").Равно(Справочники.ТипыОповещенийПользователя.Уведомление)
.Получить();

// По этому отбору проверим отсутствие нужных записей
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.НеСодержитЗаписи(Отбор);

УведомленияВызовСервера.АктуализацияУведомлений();

// А после вызова метода - присутствие
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.СодержитЗаписи(Отбор);

// Также получим сами записи используя тот же отбор
ДанныеУведомления = ЮТЗапросы.Запись(ИмяРегистра, Отбор);

ЮТест.ОжидаетЧто(ДанныеУведомления)
.Свойство("Прочитано").ЭтоЛожь()
.Свойство("Пользователь").Равно(Справочники.ГруппыОповещенийПользователей.Инженер);

КонецПроцедуры

Возможности

  • Проверка вложенных свойств:
    • Реквизит - Устанавливает имя реквизита, который необходимо проверить. Все последующие проверки будут относится к нему.
    • Свойство - Это алиас (псевдоним) для Реквизит
  • Проверки
    • Равно - Добавляет предикат, проверяющий равенство объекта (свойства) указанному значению
    • НеРавно - Добавляет предикат, проверяющий не равенство объекта (свойства) указанному значению
    • Заполнено - Добавляет предикат, проверяющий заполненность объекта (свойства)
    • Пусто - Добавляет предикат, проверяющий, что объект (свойств) не заполнено
    • Больше - Добавляет предикат, проверяющий, что значение объекта (свойства) больше указанного
    • БольшеИлиРавно - Добавляет предикат, проверяющий, что значение объекта (свойства) больше или равно указанному
    • Меньше - Добавляет предикат, проверяющий, что значение объекта (свойства) меньше указанного
    • МеньшеИлиРавно - Добавляет предикат, проверяющий, что значение объекта (свойства) меньше или равно указанному
    • ИмеетТип - Добавляет предикат, проверяющий, что значение объекта (свойства) имеет указанный тип
    • ИмеетТипОтличныйОт - Добавляет предикат, проверяющий, что значение объекта (свойства) имеет тип отличный от указанного
    • ИмеетДлину - Добавляет предикат, проверяющий, длину/размер значение объекта (свойства) на равенство указанному значению
    • ИмеетДлинуОтличнуюОт - Добавляет предикат, проверяющий, длину/размер значение объекта (свойства) на не равенство указанному значению
    • ИмеетСвойство - Добавляет предикат, проверяющий, что значение объекта (реквизита) содержит вложенное свойство
    • НеИмеетСвойства - Добавляет предикат, проверяющий, что значение объекта (реквизита) не содержит вложенное свойство
    • Содержит - Добавляет предикат, проверяющий, что значение объекта (реквизита) содержит указанное значение
    • НеСодержит - Добавляет предикат, проверяющий, что значение объекта (реквизита) не содержит указанное значение
    • СодержитСтрокуПоШаблону - Добавляет предикат, проверяющий, что строка соответствует указанному регулярному выражению
    • НеСодержитСтрокуПоШаблону - Добавляет предикат, проверяющий, что строка не соответствует указанному регулярному выражению
    • ВСписке - Добавляет условие, что проверяемое значение (или значение его свойства) входит в список значений
    • Между
      • Между/МеждуВключаяГраницы- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
        Проверяемое значение может находится на границе интервала.
      • МеждуИсключаяГраницы- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
        Проверяемое значение не может находится на границе интервала.
      • МеждуВключаяНачалоГраницы- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
        Проверяемое значение может находится на начальной границе интервала.
      • МеждуВключаяОкончаниеГраницы- Добавляет условие, что проверяемое значение (или значение его свойства) входит в заданный интервал.
        Проверяемое значение может находится на конечной границе интервала.
  • Служебные
    • Получить - Возвращает набор сформированных утверждений.
      Рекомендуется использовать этот метод, если планируется отложенная проверка предикатов. Например, вы хотите сформировать два набору предикатов и проверять их в зависимости от условия.
      Метод копирует настроенный набор утверждений в массив и возвращает его, таким образом сохраняется состояние, которое можно передавать дальше.

Возможно создавать предикаты на основании структуры - ЮТест.Предикат(Структура), например:

// Вместо
Предикат = ЮТест.Предикат()
.Свойство("Наименование").Равно(НаименованиеОбъекта)
.Свойство("Код").Равно(КодОбъекта);

// Можно использовать структур
Условия = Новый Структура("Наименование, Код", НаименованиеОбъекта, КодОбъекта);
Предикат = ЮТест.Предикат(Условия);

Примеры использования

Проверка коллекции

// Проверяет, что в коллекции есть элементы с реквизитом `Число`, значение которого равно `2`
ЮТест.ОжидаетЧто(Коллекция)
.ЛюбойЭлементСоответствуетПредикату(ЮТест.Предикат()
.Реквизит("Число").Равно(2));

// Тоже самое, что и проверка выше
ЮТест.ОжидаетЧто(Коллекция)
.Содержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2));

// Проверяет, что каждый элемент коллекции — заполненный массив
ЮТест.ОжидаетЧто(Коллекция)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.Заполнено().ИмеетТип("Массив"));

// Проверят, что в коллекции нет элементов с реквизитом `Число`, значение которого равно `2`
ЮТест.ОжидаетЧто(Коллекция)
.НеСодержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2));

Проверка нескольких условий на один реквизит

ЮТест.Предикат()
.Реквизит("Число")
.ИмеетТип("Число")
.Больше(0)
.Меньше(2)
.Равно(1)
.Получить();

Проверка вложенных свойств

ЮТест.Предикат()
.Свойство("ВложенныйОбъект.Число").Равно(2);

Использование в моках

Мокито.Обучение(Модуль)
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Сложить")))
.ВернутьРезультат(Результат1);
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Вычесть")))
.ВернутьРезультат(Результат2);

Использование в запросах

ДанныеТовара = ЮТЗапросы.Запись("Справочник.Товары", ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение));

Получение записей из базы

ДанныеТовара = ЮТЗапросы.Запись("Справочник.Товары", ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение));

Особенности

Структура предиката

Каждый предикат — это структура с полями:

  • ИмяРеквизита — имя свойства/реквизита, к которому применяется условие (или Неопределено для всего объекта)
  • ВидСравнения — тип проверки (например, "Равно", "Больше", "ИмеетТип" и др.)
  • Значение — значение для сравнения
  • ОкончаниеИнтервала — (опционально) для условий "между"

Пример структуры:

{
ИмяРеквизита: "Число",
ВидСравнения: "Больше",
Значение: 0
}

Особенности контекста

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

Внимание!

Все методы предикатов работают с глобальным контекстом. Если вы создаёте несколько предикатов подряд, они будут ссылаться на один и тот же набор условий. Чтобы получить независимые предикаты, используйте метод .Получить():

Схема "залипания" контекста

УсловиеСтрока = ЮТест.Предикат().ИмеетТип("Строка");
УсловиеЧисло = ЮТест.Предикат().ИмеетТип("Число");

Результат: обе переменные будут ссылаться на один и тот же глобальный контекст, и оба предиката будут ожидать тип "Число".

Схематично:

[Глобальный контекст]
|
+-- ЮТест.Предикат() --> инициализация контекста: []
+-- .ИмеетТип("Строка") --> контекст: [ИмеетТип: "Строка"]
|
+-- ЮТест.Предикат() --> обнуление контекста: []
+-- .ИмеетТип("Число") --> контекст: [ИмеетТип: "Число"]

Правильный способ:

УсловиеСтрока = ЮТест.Предикат().ИмеетТип("Строка").Получить();
УсловиеЧисло = ЮТест.Предикат().ИмеетТип("Число").Получить();
// Теперь это независимые массивы условий

Схематично:

[Глобальный контекст]
|
+-- ЮТест.Предикат()
+-- .ИмеетТип("Строка")
+-- .Получить() --> копия условий для строки
|
+-- ЮТест.Предикат()
+-- .ИмеетТип("Число")
+-- .Получить() --> копия условий для числа

Такая же проблема возникает и при передаче предикатов в качестве параметров методов

Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка"),
ЮТест.Предикат().ИмеетТип("Число")))
.ВернутьРезультат(Результат1);

Результат: оба параметра будут ссылаться на один и тот же глобальный контекст, и оба предиката будут ожидать тип "Число".

Правильный способ:

Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка").Получить(),
ЮТест.Предикат().ИмеетТип("Число").Получить()))
.ВернутьРезультат(Результат1);

Особенности реализации

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

Важно понимать, что сами проверки выполняются не в модуле предикатов, а в других компонентах системы. Каждый компонент может иметь свои особенности реализации и ограничения. Поэтому некоторые предикаты могут быть недоступны или работать по-разному в разных контекстах. Например, при работе с запросами нельзя использовать предикат для проверки заполненности значения, так как эта функциональность не реализована в механизме запросов.

Типичные ошибки и отладка

  • Забыли вызвать .Получить() — предикаты "залипают" и условия накапливаются в одном объекте.
  • Использование без реквизита — условие применяется к самому объекту.
  • Несовпадение типов — например, сравнение строки и числа.
  • Не все проверки поддерживаются во всех механизмах — например, не все предикаты работают в запросах.

FAQ

Q: Как проверить несколько свойств одновременно?

A: Просто добавьте несколько .Реквизит(...).Равно(...) в цепочку:

ЮТест.Предикат()
.Реквизит("Имя").Равно("Тест")
.Реквизит("Код").Равно("001");

Q: Как использовать предикаты для табличных частей?

A: Указывайте имя табличной и реквизита в методе Реквизит:

ЮТест.Предикат()
.Реквизит("Товары.Номенклатура").Равно(Товар1);

Q: Как проверить, что объект не содержит свойство?

A: Используйте .НеИмеетСвойства("ИмяСвойства").

Интеграция с другими механизмами

Предикаты используются: