1
0
mirror of https://github.com/bia-technologies/yaxunit.git synced 2025-01-05 12:50:36 +02:00

Merge pull request #306 from bia-technologies/feature/documentation

Правки документации
This commit is contained in:
Koryakin Aleksey 2024-03-28 21:28:05 +03:00 committed by GitHub
commit a72abfc1ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
101 changed files with 4230 additions and 1567 deletions

38
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,38 @@
{
"cSpell.words": [
"валидировать",
"замокированного",
"логирвания",
"Ментейнер",
"моками",
"моки",
"Мокирование",
"мокировании",
"мокированию",
"мокирования",
"мокировать",
"мокируем",
"Мокируемый",
"мокирующий",
"Мокито",
"моков",
"Неопределено",
"Переиспользование",
"помогаторы",
"пресетов",
"Регистронезависимое",
"релизный",
"сериализацию",
"сериализуемых",
"фейкового",
"фейковых",
"фича",
"юнит",
"autonumber",
"bellerage",
"mindmap",
"Wiremock",
"XDTO",
"yaxunit"
]
}

View File

@ -34,7 +34,5 @@ tags: [releases, yaxunit]
* Доработан движок для работы в режиме без использования синхронных методов
:::caution Внимание
В ближайших версиях будет удален вариант точки входа в тестовый модуль ИсполняемыеСценарии с параметрами.
:::

View File

@ -10,7 +10,7 @@ tags: [releases, yaxunit]
### Тестовые данные
* Загрузка данных из табличного документа или таблицы markdown [#133](https://github.com/bia-technologies/yaxunit/issues/133), [документация](https://bia-technologies.github.io/yaxunit/docs/user-api/test-data/#%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B8%D0%B7-%D0%BC%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2). Спасибо [@dlyubanevich](https://github.com/dlyubanevich)
* Загрузка данных из табличного документа или таблицы markdown [#133](https://github.com/bia-technologies/yaxunit/issues/133), [документация](https://bia-technologies.github.io/yaxunit/docs/features/test-data/#%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BA%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B8%D0%B7-%D0%BC%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2). Спасибо [@dlyubanevich](https://github.com/dlyubanevich)
* Добавить возможность указывать тип фикции [#121](https://github.com/bia-technologies/yaxunit/issues/121)
* Добавить возможность получать случайное значение перечисления [#132](https://github.com/bia-technologies/yaxunit/issues/132)
* Тестовые данные, добавить метод СлучайныйИдентификатор [#102](https://github.com/bia-technologies/yaxunit/issues/102)

View File

@ -1,5 +1,5 @@
alkoleft:
name: alkoleft
title: Разработчик YaxUnit
url: https://github.com/alkoleft
image_url: https://github.com/alkoleft.png
name: alkoleft
title: Разработчик YAxUnit
url: https://github.com/alkoleft
image_url: https://github.com/alkoleft.png

View File

@ -0,0 +1,7 @@
# Кулинарная книга тестов
* как тестировать веб-сервисы
* как тестировать локальные функции
* как подключать свои плагины/шаги/методы специфичные для своей базы
* поддержка англ.языка, линукса, Конфигуратора
* что за и зачем нужен Макито

View File

@ -0,0 +1,601 @@
---
tags: [Начало, Утверждения]
---
# Базовые утверждения
Утверждения для проверки значений
Доступ к утверждениям обеспечивает метод [`ЮТест.ОжидаетЧто`](/api/ЮТест#ожидаетчто), который возвращает инициализированный модуль [`ЮТУтверждения`](/api/ЮТУтверждения), реализующий работу с утверждениями.
:::tip
Не рекомендуется обращаться к модулю `ЮТУтверждения` напрямую, используйте `ЮТест.ОжидаетЧто`
:::
* Реализован достаточный набор проверок значений
* [Сравнение значений](#сравнение-значений)
* [Методы позиционирования](#методы-позиционирования)
* [Проверка заполненности](#проверка-заполненности)
* [Проверка строк](#проверка-строк)
* [Проверка вхождения значения в интервал](#проверка-вхождения-значения-в-интервал)
* [Проверка типа значения](#проверка-типа-значения)
* [Проверка выполнения метода](#проверка-выполнения-метода)
* [Проверка наличия свойств/реквизитов](#проверка-наличия-свойствреквизитов)
* [Проверка коллекции](#проверка-коллекции)
* [Проверка на соответствие набору условий, предикату](#проверка-на-соответствие-набору-условий-предикату)
* Большая часть методов - это сравнения фактического и ожидаемого результатов, но есть несколько методов настройки
* [`Что`](/api/ЮТУтверждения#что) - устанавливает проверяемый объект. Все дальнейшие проверки будут выполняется с этим объектом
* [`Метод`](/api/ЮТУтверждения#метод) - устанавливает имя и параметры проверяемого метода. Для проверки методов имеются утверждения [`ВыбрасываетИсключение`](/api/ЮТУтверждения#выбрасываетисключение) и [`НеВыбрасываетИсключение`](/api/ЮТУтверждения#невыбрасываетисключение)
* [`Параметр`](/api/ЮТУтверждения#параметр) - добавляет параметр метода. Создан для удобства установки параметров проверяемого метода
* [`Свойство`](/api/ЮТУтверждения#свойство) - устанавливает проверяемое свойство и проверяет его наличие.
* Последующие проверки, вызванные после этого метода, будут относиться к свойству объекта.
Например, `ЮТест.ОжидаетЧто(Контекст).Свойство("ИмяМетода").Равно("МетодБезИсключение")` эквивалентно проверке `Контекст.ИмяМетода = "МетодБезИсключение"`
* Методы работы со свойствами позволяют указывать цепочку свойств (доступ к вложенным свойствам через точку)
Например:
* `Свойство("Контекст.ИмяМетода")` - вложенное свойство
* `ИмеетСвойство("Контекст.ПараметрыМетода[0]")` - элемент вложенной коллекции
* `НеИмеетСвойства("[0].Свойство")` - свойство элемента коллекции
* Все методы имеют параметр `ОписаниеПроверки` для детального описания.
## Доступные методы
:::tip
Полный и актуальный набор методов смотрите в описании API
:::
### Сравнение значений
* [`Равно`](/api/ЮТУтверждения#равно) - проверка на равенство конкретному значению. Для сериализуемых объектов идет сравнение по значению
* [`НеРавно`](/api/ЮТУтверждения#неравно) - проверка на не равенство конкретному значению. Для сериализуемых объектов идет сравнение по значению
* [`Больше`](/api/ЮТУтверждения#больше) - проверяемое значение должно быть больше указанного
* [`БольшеИлиРавно`](/api/ЮТУтверждения#большеилиравно) - проверяемое значение должно быть больше или равно указанному
* [`Меньше`](/api/ЮТУтверждения#меньше) - проверяемое значение должно быть меньше указанного
* [`МеньшеИлиРавно`](/api/ЮТУтверждения#меньшеилиравно) - проверяемое значение должно быть меньше или равно указанному
* [`ЭтоНеопределено`](/api/ЮТУтверждения#этонеопределено) - проверяемое значение должно быть равно `Неопределено`
* [`ЭтоНеНеопределено`](/api/ЮТУтверждения#этоненеопределено) - проверяемое значение должно быть не равно `Неопределено`
* [`ЭтоNull`](/api/ЮТУтверждения#этоnull) - проверяемое значение должно быть равно `Null`
* [`ЭтоНеNull`](/api/ЮТУтверждения#этонеnull) - проверяемое значение должно быть не равно `Null`
* [`ЭтоИстина`](/api/ЮТУтверждения#этоистина) - проверяемое значение должно быть истиной
* [`ЭтоНеИстина`](/api/ЮТУтверждения#этонеистина) - проверяемое значение не должно быть истиной
* [`ЭтоЛожь`](/api/ЮТУтверждения#этоложь) - проверяемое значение должно быть ложью
* [`ЭтоНеЛожь`](/api/ЮТУтверждения#этонеложь) - проверяемое значение не должно быть ложью
* [`ВСписке`](/api/ЮТУтверждения#этонеложь) - проверяемое значение должно входить в список указанных.
```bsl
Значение = 1;
ЮТУтверждения.Что(Значение)
.Равно(1)
.НеРавно(2)
.Больше(0)
.БольшеИлиРавно(-10)
.Меньше(10)
.МеньшеИлиРавно(2)
.ЭтоНеИстина()
.ЭтоНеЛожь()
.ВСписке(ЮТКоллекция.ЗначениеВМассиве(0, 1, 2));
```
#### Особенности сравнения
* При сравнении значений сравниваются их типы. Это отличается от стандартного поведения 1С:Предприятие, где некоторые значения разных типов могут быть равны, например `1 = Истина`
```bsl
ЮТест.ОжидаетЧто(1 = Истина).ЭтоИстина(); // Проверка успешна
ЮТест.ОжидаетЧто(1).Равно(Истина); // Проверка провалится
```
* Для большинства типов данных реализовано сравнение "по значению", когда сравниваются (рекурсивно) значения реквизитов объекта.
* Поддерживается сравнение структур и соответствий.
```bsl
Значение1 = Новый Структура("Реквизит", 1);
Значение2 = Новый Структура("Реквизит", 1);
ЮТест.ОжидаетЧто(Значение1 = Значение2).ЭтоИстина(); // Проверка провалится
ЮТест.ОжидаетЧто(Значение1).Равно(Значение2); // Проверка успешна
```
* Поддерживается сравнение массивов, таблиц и табличных документов.
* Поддерживается сравнение значений упакованных в `ХранилищеЗначения`.
* Для прочих сериализуемых значений (не примитивов) сравнение происходит через сериализацию в JSON.
### Методы позиционирования
Любое из утверждений можно применять как к самому анализируемому объекту, так и к его свойствам, реквизитам, вложенным элементам.
Например, проверить, наличие заполненной табличной части документа используя выражение
```bsl
ЮТест.ОжидаетЧто(Документ)
.Свойство("Товары").Заполнено()
.Свойство("Товары[0].Номенклатура").Равно(ОжидаемыйТовар)
;
```
Благодаря методам [`Свойство`](/api/ЮТУтверждения#свойство) и [`Элемент`](/api/ЮТУтверждения#элемент) можно позиционировать (переключать объект проверки) утверждения на вложенный реквизит/элемент.
Методы [`Свойство`](/api/ЮТУтверждения#свойство) и [`Элемент`](/api/ЮТУтверждения#элемент) переключают объект проверки для последующих методов-утверждений. Они будут применяться к значению свойства или элементу коллекции. Но не переключает сам контекст на это значение, а всегда применяется к исходному объекту, помещенному в [`ОжидаетЧто`](/api/ЮТест#ожидаетчто).
* [`Свойство`](/api/ЮТУтверждения#свойство) - проверяет наличие свойства и позиционирует дальнейшие проверки на указанном свойстве.
* [`Элемент`](/api/ЮТУтверждения#элемент) - проверяет наличие элемента коллекции и позиционирует дальнейшие проверки на указанном элементе. На самом деле это просто алиас (псевдоним) для метода `Свойство` и выполняет он тоже самое.
* [`Объект`](/api/ЮТУтверждения#объект) - позиционирует дальнейшие проверки на объекте, указанном в методе [`ОжидаетЧто`](/api/ЮТест#ожидаетчто).
* [`НетСвойства`](/api/ЮТУтверждения#нетсвойства) - проверяет отсутствие свойства и позиционирует дальнейшие проверки на объекте, указанном в методе [`ОжидаетЧто`](/api/ЮТест#ожидаетчто).
:::caution
Методы [`Свойство`](/api/ЮТУтверждения#свойство) и [`Элемент`](/api/ЮТУтверждения#элемент) всегда вычисляют путь от объекта указанного в [`ОжидаетЧто`](/api/ЮТест#ожидаетчто), они не учитывают предыдущие изменения позиции.
Например, чтобы проверить значение реквизита `Реквизит.ВложенныйРеквизит`
```bsl title="Неправильно"
Объект = Новый Структура("Реквизит", Новый Структура("ВложенныйРеквизит", 1));
ЮТест.ОжидаетЧто(Объект)
.Свойство("Реквизит")
.Свойство("ВложенныйРеквизит").Равно(1) // Выбросит исключение, тк объект не содержит свойство "ВложенныйРеквизит"
;
```
Правильно в таком примере указывать сразу полный путь для свойства.
```bsl title="Правильно"
ЮТест.ОжидаетЧто(Объект)
.Свойство("Реквизит.ВложенныйРеквизит").Равно(1)
;
```
:::
Методы [`Свойство`](/api/ЮТУтверждения#свойство) и [`Элемент`](/api/ЮТУтверждения#элемент), принимают на вход не просто имя реквизита, а выражение, для перехода к нужному свойству, таким образом вы можете позиционироваться:
* На свойствах объекта
```bsl
ЮТест.ОжидаетЧто(Объект)
.Свойство("Реквизит").Равно(ОжидаемоеЗначение)
```
* На вложенных свойствах объекта `Свойство("Реквизит.ВложенныйРеквизит")`
```bsl
ЮТест.ОжидаетЧто(Объект)
.Свойство("Реквизит.ВложенныйРеквизит").Равно(ОжидаемоеЗначение)
```
* На элементах коллекций
```bsl
ЮТест.ОжидаетЧто(Документ.Товары)
.Свойство("[0].Товар").Равно(ОжидаемоеЗначение);
// Или
ЮТест.ОжидаетЧто(Документ)
.Свойство("Товары[0].Товар").Равно(ОжидаемоеЗначение);
```
* Можно позиционировать на элементах коллекции с конца
```bsl
ЮТест.ОжидаетЧто(Документ.Товары)
.Свойство("[-1].Товар").Равно(ОжидаемоеЗначение) // Последний элемент;
.Элемент(-1).ИмеетСвойство("Товар") // Последний элемент
```
### Проверка заполненности
* [`Заполнено`](/api/ЮТУтверждения#заполнено) - проверяет заполненность значения
```bsl
ЮТест.ОжидаетЧто(ОбъектПроверки)
.Заполнено()
.Свойство("Номер").Заполнено();
```
* [`НеЗаполнено`](/api/ЮТУтверждения#незаполнено) - проверяет незаполненность значения
```bsl
Значения = ЮТКоллекции.ЗначениеВМассиве(0, "", " ", Неопределено, Null, '00010101', Новый Структура(), Справочники.Товары.ПустаяСсылка());
Для каждого Значение Из Значения Цикл
ЮТест.ОжидаетЧто(Значение)
.НеЗаполнено()
КонецЦикла;
```
* [`Существует`](/api/ЮТУтверждения#существует) - проверяет существование (не равно `Null` и `Неопределено`) значения
```bsl
ЮТест.ОжидаетЧто(ВыборкаЗапроса)
.Свойство("Номер").Существует();
```
* [`НеСуществует`](/api/ЮТУтверждения#несуществует) - проверяет не существование (не равно `Null` и `Неопределено`) значения
```bsl
ЮТест.ОжидаетЧто(ВыборкаЗапроса)
.Свойство("Номер").НеСуществует();
```
### Проверка строк
* [`ИмеетДлину`](/api/ЮТУтверждения#имеетдлину) - проверяет, что строка имеет указанную длину
* [`ИмеетДлинуБольше`](/api/ЮТУтверждения#имеетдлинубольше) - проверяет, что длин строки больше указанной
* [`ИмеетДлинуМеньше`](/api/ЮТУтверждения#имеетдлинуменьше) - проверяет, что длина строки меньше указанной
* [`НеИмеетДлину`](/api/ЮТУтверждения#неимеетдлину) - проверяет, что длина строки отличается от указанной
* [`Содержит`](/api/ЮТУтверждения#содержит) - проверяемая строка содержит указанную подстроку
* [`НеСодержит`](/api/ЮТУтверждения#несодержит) - проверяемая строка не содержит указанную подстроку
* [`НачинаетсяС`](/api/ЮТУтверждения#начинаетсяс) - проверяемая строка начинается с указанной строки
* [`ЗаканчиваетсяНа`](/api/ЮТУтверждения#заканчиваетсяна) - проверяемая строка заканчивается на указанную строку
```bsl
ПроверяемоеЗначение = "Ох, нелегкая это работа - Из болота тащить бегемота";
ЮТест.ОжидаетЧто("ПроверяемоеЗначение")
.ИмеетДлинуБольше(10)
.ИмеетДлинуМеньше(100)
.ИмеетДлину(51)
.Содержит("работа")
.НеСодержит("зарплата")
.НачинаетсяС("Ох")
.ЗаканчиваетсяНа("мота")
```
* [`СодержитСтрокуПоШаблону`](/api/ЮТУтверждения#содержитстрокупошаблону) - проверяемая строка содержит подстроку, соответствующую регулярному выражению
* [`НеСодержитСтрокуПоШаблону`](/api/ЮТУтверждения#несодержитстрокупошаблону) - проверяемая строка не содержит подстроку, соответствующую регулярному выражению
```bsl
ПроверяемоеЗначение = "Ох, нелегкая это работа - Из болота тащить бегемота";
ЮТест.ОжидаетЧто("ПроверяемоеЗначение")
.СодержитСтрокуПоШаблону("^[а-яА-Я\s,\-]*$") // Строка содержит только кириллицу, пробелы и знаки пунктуации
.НеСодержитСтрокуПоШаблону("\d") // Не содержит цифр
```
### Проверка вхождения значения в интервал
* [`МеждуВключаяГраницы`](/api/ЮТУтверждения#междувключаяграницы) - проверяемое значение находиться в указанному интервале (включая границы)
* [`МеждуИсключаяГраницы`](/api/ЮТУтверждения#междуисключаяграницы) - проверяемое значение находиться в указанному интервале (исключая границы)
* [`МеждуВключаяНачалоГраницы`](/api/ЮТУтверждения#междувключаяначалограницы) - проверяемое значение находиться в указанному интервале (включая левую границу и исключая правую)
* [`МеждуВключаяОкончаниеГраницы`](/api/ЮТУтверждения#междувключаяокончаниеграницы) - проверяемое значение находиться в указанному интервале (исключая левую границу и включая правую)
```bsl
ЮТест.ОжидаетЧто(10)
.МеждуВключаяГраницы(0, 10)
.МеждуИсключаяГраницы(0, 100);
ЮТест.ОжидаетЧто(ТекущаяДата())
.МеждуВключаяНачалоГраницы(НачалоДня, НачалоСледующегоДня);
```
### Проверка типа значения
* [`ИмеетТип`](/api/ЮТУтверждения#имееттип) - проверяемое значение должно иметь указанный тип
* [`НеИмеетТип`](/api/ЮТУтверждения#неимееттип) - тип проверяемого значения должен отличаться от указанного
В качестве аргумента можно указывать
* Имя типа строкой или список имен типов через запятую.
`ИмеетТип("Строка").ИмеетТип("Строка, Число")`
* Конкретный тип.
`ИмеетТип(Тип("Строка"))`
* Описание типов.
`ИмеетТип(Новый ОписаниеТипов("Строка, Число"))`
### Проверка выполнения метода
API для проверки, выбрасываемых методом проверяемого объекта исключения.
Первоначально необходимо описать какой метод нужно проверить, к какому объекту он относится и с какими параметрами его необходимо вызвать. Для этих целей необходимо использовать:
* [`ЮТест.ОжидаетЧто`](/api/ЮТест#ожидаетчто) - устанавливает владельца метода
* [`Метод`](/api/ЮТУтверждения#метод) - устанавливает имя и параметры проверяемого метода
* [`Параметр`](/api/ЮТУтверждения#параметр) - добавляет параметр метода. Создан для удобства установки параметров проверяемого метода
После формируем ожидание - должен или нет методы выбросить исключение и с каким текстом.
* [`ВыбрасываетИсключение`](/api/ЮТУтверждения#выбрасываетисключение) - выполняет настроенный метод объекта с параметрами и проверяет, что метод выбросит исключение и текст исключения содержит(включает) указанную в строку. При необходимости можно настроить (передать параметр), чтобы метод выполнился **в транзакции**.
```bsl
ЮТест.ОжидаетЧто(Документ) // Объект, метод которого проверяем
.Метод("Записать").Параметр(РежимЗаписиДокумента.Проведение) // Указываем проверяемый метод и параметры вызова
.ВыбрасываетИсключение("Значение поля ""Дата"" не может быть пустой датой") // Утверждение-проверка, исключение должно содержать указанный текст.
.ВыбрасываетИсключение("""Дата""") // Можно указать только часть исключения, проверить вхождение указанной строки.
.ВыбрасываетИсключение("""Дата""", , Истина) // Выполнение указанного метода в транзакции
```
:::info Вопрос
Можно ли **не указывать** текст исключения?
**Ответ:** Нельзя, вы должны конкретизировать свои ожидания, зафиксировать, какую ошибку ждете, иначе поведение может измениться и тест это не отработает.
:::
* [`НеВыбрасываетИсключение`](/api/ЮТУтверждения#невыбрасываетисключение) - выполняет настроенный метод объекта с параметрами и проверяет, что метод не выбросит исключение или выбросит исключения с текстом отличным от указанного. При необходимости можно настроить (передать параметр), чтобы метод выполнился **в транзакции**.
```bsl
ЮТест.ОжидаетЧто(Документ) // Объект, метод которого проверяем
.Метод("Записать").Параметр(РежимЗаписиДокумента.Проведение) // Указываем проверяемый метод и параметры вызова
.НеВыбрасываетИсключение() // Утверждение-проверка, метод выполниться без ошибки.
.НеВыбрасываетИсключение("""Дата""") // Утверждение-проверка, метод выполниться без ошибки, либо с ошибкой, которая не содержит "Дата".
.НеВыбрасываетИсключение("""Дата""", , Истина) // Выполнение указанного метода в транзакции
```
:::caution Не злоупотребляйте проверкой `НеВыбрасываетИсключение`
Многие используют это утверждения, для вызова чуть ли не каждого проверяемого метода.
Этого делать не стоит.
Это утверждение должно редко применяться, если ваш метод упадет, это и так будет отражено в отчете.
Один из случаев когда оно может применяться, вы используете его **перед** проверками на исключения, чтобы убедится, что метод рабочий.
```bsl
Документ = СоздатьДокумент();
Утверждение = ЮТест.ОжидаетЧто(Документ)
.Метод("Записать").Параметр(РежимЗаписиДокумента.Проведение)
.НеВыбрасываетИсключение("""Дата"""); // Проверим, что при нормальном заполнении ошибки нет
Документ.Дата = Неопределено;
Утверждение.ВыбрасываетИсключение("""Дата""", "Документ проведен при незаполненной дате");
```
:::
:::caution Нельзя явно вызывать проверяемый метод
Хочу обратить внимание. Когда вы проверяете метод на исключения/их отсутствие вы должны указать:
```bsl
ЮТест.ОжидаетЧто(Документ) // 1. Владельца метода
.Метод("Записать") // 2. Имя метода
.Параметр(РежимЗаписиДокумента.Проведение) // 3. Параметры вызова метода
// Явно вызывать метод нельзя.
ЮТест.ОжидаетЧто(Документ)
.Метод(Документ.Записать(РежимЗаписиДокумента.Проведение)) // Так делать нельзя, так не сработает проверка.
```
Вы **не должны** явно вызывать метод, это должен сделать тестовый движок, чтобы поймать и проанализировать ошибку.
:::
### Проверка наличия свойств/реквизитов
* [`ИмеетСвойство`](/api/ЮТУтверждения#имеетсвойство) - проверяемый объект должен содержать указанное свойство
* [`НеИмеетСвойства`](/api/ЮТУтверждения#неимеетсвойства) - проверяемый объект не содержит указанное свойство
* [`ИмеетСвойстваРавные`](/api/ЮТУтверждения#имеетсвойстваравные) - проверяемый объект должен содержать указанный набор свойств/реквизитов и значений
Поддерживается проверка наличия свойство для различных объектов: структур, массивов, таблиц, документов и тд.
Вы можете проверить наличие свойства не только у текущего объекта, но и у его дочерних.
Например, `ИмеетСвойство("Родитель.Наименование")`, будет выполнена проверка наличия свойства `Родитель` у текущего объекта и свойства `Наименование` у родителя.
Также можно проверять наличие свойств у коллекций.
Например, `ИмеетСвойство("Товары[0].Наименование")`, утверждение проверит наличие свойства `Товары`, наличие у него первого элемента и наличие свойства `Наименование` у этого элемента.
```bsl
Документ = Документы.ПриходТовара.СоздатьДокумент();
ЮТест.ОжидаетЧто(Документ)
.ИмеетСвойство("Товары[0]")
.Свойство("Товары[0]")
.ИмеетСвойство("Товар")
.ИмеетСвойство("Количество")
.ИмеетСвойство("Цена");
```
Как вы могли заменить в качестве имени свойства указывается выражение, такое же как и обычном коде.
```bsl
Документ = Документы.ПриходТовара.СоздатьДокумент();
Свойство = Документ.Товары[0].Наименование;
ЮТест.ОжидаетЧто(Документ)
.Свойство("Товары[0].Наименование");
// И там и там для того, чтобы добраться до наименования используется одно и тоже выражение.
```
### Проверка коллекции
* [`ИмеетДлину`](/api/ЮТУтверждения#имеетдлину) - проверяет, что коллекция имеет указанный размер
* [`ИмеетДлинуБольше`](/api/ЮТУтверждения#имеетдлинубольше) - проверяет, что коллекция имеет размер, который больше указанного
* [`ИмеетДлинуМеньше`](/api/ЮТУтверждения#имеетдлинуменьше) - проверяет, что коллекция имеет размер, который меньше указанного
* [`НеИмеетДлину`](/api/ЮТУтверждения#неимеетдлину) - проверяет, что размер коллекции отличается от указанного
```bsl
Коллекция = ЮТКоллекции.ЗначениеВМассиве(1, 2, 3);
ЮТест.ОжидаетЧто(Коллекция)
.ИмеетДлину(3)
.ИмеетДлинуБольше(2)
.ИмеетДлинуМеньше(4)
.НеИмеетДлину(4)
;
```
* [`Содержит`](/api/ЮТУтверждения#содержит) - проверяемая коллекция должна содержать указанный элемент
* [`НеСодержит`](/api/ЮТУтверждения#несодержит) - проверяемая коллекция не должна содержать указанный элемент
```bsl
Коллекция = ЮТКоллекции.ЗначениеВМассиве(1, 2, 3);
ЮТест.ОжидаетЧто(Коллекция)
.Содержит(1) // Конкретное значение
.Содержит(ЮТест.Предикат() // Значение по условиям
.ИмеетТип("Число")
.Больше(2))
.НеСодержит(0)
.НеСодержит(ЮТест.Предикат() // Не содержит строк
.ИмеетТип("Строка"))
;
```
* [`КаждыйЭлементСодержитСвойство`](/api/ЮТУтверждения#каждыйэлементсодержитсвойство) - проверяет, что каждый элемент коллекции имеет указанное свойство
* [`КаждыйЭлементСодержитСвойствоСоЗначением`](/api/ЮТУтверждения#каждыйэлементсодержитсвойствосозначением) - проверяет, что каждый элемент коллекции имеет указанное свойство, которое равно ожидаемому значению
* [`ЛюбойЭлементСодержитСвойство`](/api/ЮТУтверждения#любойэлементсодержитсвойство) - проверяет, что в коллекции есть элемент содержащий указанное свойство
* [`ЛюбойЭлементСодержитСвойствоСоЗначением`](/api/ЮТУтверждения#любойэлементсодержитсвойствосозначением) - проверяет, что в коллекции есть элемент содержащий указанное свойство, которое равно ожидаемому значению
```bsl
Коллекция = ЮТКоллекции.ЗначениеВМассиве(
Новый Структура("С1", 1),
Новый Структура("С1, С2", 1, 2)
);
ЮТест.ОжидаетЧто(Коллекция)
.КаждыйЭлементСодержитСвойство("С1") // Проверка, что все элементы коллекции содержат свойство `С1`
.КаждыйЭлементСодержитСвойствоСоЗначением("С1", 1) // Проверка, что все элементы коллекции содержат свойство `С1` со значением `1`
.ЛюбойЭлементСодержитСвойство("С2") // Проверка, что хотя бы один элемент коллекции содержат свойство `С2`
.ЛюбойЭлементСодержитСвойство("С2", 2) // Проверка, что хотя бы один элемент коллекции содержат свойство `С2` со значением `2`
;
```
* [`КаждыйЭлементСоответствуетПредикату`](/api/ЮТУтверждения#каждыйэлементсоответствуетпредикату) - проверяет, что элементы коллекции соответствуют переданным условиям
```bsl
Коллекция = ЮТКоллекции.ЗначениеВМассиве(1, 2, 3);
ЮТест.ОжидаетЧто(Коллекция)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.ИмеетТип("Число") // Все элементы это числа
.НеРавно(0)) // и каждый не равен `0`
```
* [`ЛюбойЭлементСоответствуетПредикату`](/api/ЮТУтверждения#любойэлементсоответствуетпредикату) - проверяет, что коллекция содержит элемент, который соответствует переданным условиям
```bsl
Коллекция = ЮТКоллекции.ЗначениеВМассиве(1, 2, 3);
ЮТест.ОжидаетЧто(Коллекция)
.ЛюбойЭлементСоответствуетПредикату(ЮТест.Предикат()
.ИмеетТип("Число") // Есть элемент, который является числом
.Больше(2)) // и больше `2`
```
### Проверка на соответствие набору условий, предикату
* [`Содержит`](/api/ЮТУтверждения#содержит)/[`ЛюбойЭлементСоответствуетПредикату`](/api/ЮТУтверждения#любойэлементсоответствуетпредикату) - проверяемая коллекция должна содержать элемент, который соответствует переданным условиям
* [`НеСодержит`](/api/ЮТУтверждения#несодержит) - проверяемая коллекция не должна содержать элемент, который соответствует переданным условиям
* [`КаждыйЭлементСоответствуетПредикату`](/api/ЮТУтверждения#каждыйэлементсоответствуетпредикату) - проверяет, что элементы коллекции соответствуют переданным условиям
* [`СоответствуетПредикату`](/api/ЮТУтверждения#соответствуетпредикату) - проверяет, что объект или его свойство соответствует набору условий
Работа и суть предикатов описана в [отдельной статье](../predicates.md).
#### Предикаты позволяют проверить каждый элемент коллекцию на соответствие набору условий
Предположим, нам необходимо проверить, что движения документа выполненные по регистру делают приход. Тогда тест может выглядеть так.
```bsl
Документ = СоздатьИПровестиДокумент(...);
Движения = ЮТЗапросы.ДвиженияДокумента(Документ, "ОстаткиТоваров");
ЮТест.ОжидаетЧто(Движения)
.Заполнено()
.ИмеетДлину(Документ.Товары.Количество())
.КаждыйЭлементСоответствуетПредикату(
ЮТест.Предикат()
.Реквизит("ВидДвижения").Равно(ВидДвиженияНакопления.Приход)
.Реквизит("Количество").Больше(0)
)
;
// Условия предиката будут применены для каждой записи движений. Если ожидания не совпадут, то будет выброшена ошибка.
```
#### Предикаты позволяют проверить наличие элемента по сложным условиям
```bsl
Документ = СоздатьИПровестиДокумент(...);
Движения = ЮТЗапросы.ДвиженияДокумента(Документ, "ОстаткиТоваров");
ЮТест.ОжидаетЧто(ПолучитьСообщенияПользователю())
.Заполнено()
.Содержит(
ЮТест.Предикат()
.Реквизит("Текст")
.НачинаетсяС("Начало")
.ЗаканчиваетсяНа("окончание")
.НеСодержит("исключение")
)
;
```
## Примеры
### Базовые проверки
Предположим, у нас имеется функция `Сложение`, которая выполняет сложение двух параметров.
Проверки для нее могут выглядеть следующим образом:
```bsl title="Тест для 'Сложение'"
Результат = Сложение(2, 3);
ЮТест.ОжидаетЧто(Результат, "Сложение чисел") // Используя модуль утверждений установим проверяемое значение и пояснение
.ИмеетТип("Число") // Проверим тип
.Заполнено() // Заполненность проверяемого значения
.Больше(0) // Сравним с нулем
.Равно(5); // Проверим ожидаемый результат
Результат = Сложение("2", 3);
ЮТест.ОжидаетЧто(Результат, "Сложение строки и числа")
.ИмеетТип("Строка")
.Равно("23");
Результат = Сложение('2000010101', 3);
ЮТест.ОжидаетЧто(Результат, "Сложение даты и числа")
.Равно('2000010101000003');
```
В примере, я на каждом этапе уменьшаю количество проверок. В реальном тесте хватит проверки на равенство, остальные проверки здесь для того, чтобы показать возможности механизма.
### Проверка сложного объекта
```bsl
ЮТест.ОжидаетЧто(Ссылка, "Созданный объект")
.Заполнено()
.ИмеетТип("ДокументСсылка.ПриходТовара")
.Свойство("Номер").Заполнено()
.Свойство("Дата").Заполнено()
.Свойство("Поставщик").Равно(Поставщик)
.Свойство("Склад").Равно(Склад)
.Свойство("Валюта").Равно(Валюта)
.Свойство("Организация").Равно(Организация)
.Свойство("Товары").ИмеетДлину(2)
.Свойство("Товары[0].Товар").Равно(Товар1)
.Свойство("Товары[0].Цена").Больше(0)
.Свойство("Товары[0].Количество").Больше(0)
.Свойство("Товары[0].Сумма").Равно(100)
.Свойство("Товары[-1].Товар").Равно(Товар2)
.Свойство("Товары[-1].Сумма").Равно(200)
.Свойство("Проведен").ЭтоИстина()
```
### Проверка вызова метода
```bsl
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодБезИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение"))
.НеВыбрасываетИсключение()
.НеВыбрасываетИсключение("Ожидаемое исключение");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение", 2))
.ВыбрасываетИсключение("Слишком много фактических параметров");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение"))
.ВыбрасываетИсключение("Исключение");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодБезИсключение")
.ВыбрасываетИсключение("Недостаточно фактических параметров");
```
### Проверка соответствия предикату
```bsl
Дата = ЮТест.Данные().СлучайнаяДата();
Объект = Новый Структура;
Объект.Вставить("Число", 1);
Объект.Вставить("Строка", "1");
Объект.Вставить("Дата", Дата);
Объект.Вставить("Массив", ЮТКоллекции.ЗначениеВМассиве(1, "1"));
ПроверкаЧисла = ЮТест.Предикат().Реквизит("Число")
.ИмеетТип(Тип("Число"))
.БольшеИлиРавно(1)
.МеньшеИлиРавно(10)
.Получить();
ПроверкаДаты = ЮТест.Предикат().Реквизит("Дата")
.ИмеетТип(Новый ОписаниеТипов("Дата"))
.Равно(Дата)
.Получить();
ЮТест.ОжидаетЧто(Объект)
.СоответствуетПредикату(ЮТест.Предикат()
.Заполнено()
.ИмеетТип("Структура"))
.СоответствуетПредикату(ПроверкаЧисла)
.СоответствуетПредикату(ПроверкаДаты)
;
```
### Проверка элементов коллекции на соответствие предикату
```bsl
ТаблицаРезультатов = ЮТест.Данные().ЗагрузитьИзМакета("ОбщийМакет.ЮТ_МакетТестовыхДанных.R2C1:R5C11", ОписанияТипов);
ЮТест.ОжидаетЧто(ТаблицаРезультатов)
.ИмеетТип("Массив")
.ИмеетДлину(3)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.Реквизит("Товар").Заполнено().ИмеетТип("СправочникСсылка.Товары")
.Реквизит("Период").Заполнено().ИмеетТип("Дата")
.Реквизит("Количество").Заполнено().ИмеетТип("Число")
.Реквизит("Цена").Заполнено().ИмеетТип("Число")
);
ЮТест.ОжидаетЧто(ТаблицаРезультатов)
.Содержит(ЮТест.Предикат()
.Реквизит("Товар").Равно(Товар1)) // Таблица содержит строку с Товар1
.Содержит(ЮТест.Предикат(Новый Структура("Товар", Товар2))) // Таблица содержит строку с Товар2
.Содержит(ЮТест.Предикат()
.Реквизит("Товар").Равно(Товар3) // Таблица содержит строку с Товар3
.Реквизит("Количество").Равно(2)) // и количеством 2
```

View File

@ -0,0 +1,79 @@
---
tags: [Начало, Утверждения, ДанныеИБ]
---
# Утверждения для проверки наличия данных информационной базы
Большая часть тестируемых методов так или иначе оставляет свой след в информационной базе, создает или изменяет записи - документы, регистры, справочники и так далее.
Для проверки правильности работы таких методов нам необходимо проверить, что изменилось в базе. В этом могут помочь утверждения работающие с записями ИБ.
Доступ к этим утверждениям обеспечивает метод [`ЮТест.ОжидаетЧтоТаблицаБазы`](/api/ЮТест#ожидаетчтотаблицабазы), который возвращает инициализированный модуль [`ЮТУтвержденияИБ`](/api/ЮТУтвержденияИБ), реализующий работу с утверждениями для ИБ.
Утверждения ИБ позволяют проверить наличие и отсутствие записей по различным условиям, которые задаются с помощью [предикатов](../predicates.md)
```bsl
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.НеСодержитЗаписи(); // В базе нет товаров
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.СодержитЗаписи(); // В базе есть товары
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.СодержитЗаписи(ЮТест.Предикат() // В базе есть товары определенного поставщика
.Реквизит("Поставщик").Равно(ДанныеСправочника.Поставщик));
ЮТест.ОжидаетЧтоТаблицаБазы("РегистрСведений.КурсыВалют")
.НеСодержитЗаписи(ЮТест.Предикат() // Курс валюты не установлен
.Реквизит("Валюта").Равно(ДанныеРегистра.Валюта)
.Реквизит("Период").БольшеИлиРавно(ДанныеРегистра.Период));
```
## Доступные методы
* [`СодержитЗаписи`](/api/ЮТУтвержденияИБ#содержитзаписи) - проверяет наличие записей по условиям
* [`НеСодержитЗаписи`](/api/ЮТУтвержденияИБ#несодержитзаписи) - проверяет отсутствие записей по условиям
* [`СодержитЗаписиСНаименованием`](/api/ЮТУтвержденияИБ#содержитзаписиснаименованием) - проверяет наличие в таблице записей с указанным наименованием
* [`СодержитЗаписиСКодом`](/api/ЮТУтвержденияИБ#содержитзаписискодом) - проверяет наличие в таблице записей с указанным кодом
* [`СодержитЗаписиСНомером`](/api/ЮТУтвержденияИБ#содержитзаписисномером) - проверяет наличие в таблице записей с указанным номером
* [`НеСодержитЗаписиСНаименованием`](/api/ЮТУтвержденияИБ#несодержитзаписиснаименованием) - проверяет отсутствие в таблице записей с указанным наименованием
* [`НеСодержитЗаписиСКодом`](/api/ЮТУтвержденияИБ#несодержитзаписискодом) - проверяет отсутствие в таблице записей с указанным кодом
* [`НеСодержитЗаписиСНомером`](/api/ЮТУтвержденияИБ#несодержитзаписисномером) - проверяет отсутствие в таблице записей с указанным номером
## Примеры
```bsl title="Тест создания справочника"
// Проверка и подготовка контекста
АртикулТовара = ЮТест.Данные().СлучайнаяСтрока();
УсловиеПоискаТовара = ЮТест.Предикат()
.Реквизит("Артикул").Равно(АртикулТовара)
.Получить(); // Необходимо для сохранения сформированного предиката в переменную
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары", "Товар уже существует в базе")
.НеСодержитЗаписей(УсловиеПоискаТовара);
// Выполнение тестируемого метода
СоздатьТовар(АртикулТовара);
// Проверка результата
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары", "Товар не был создан")
.СодержитЗаписи(УсловиеПоискаТовара);
// Проверим заполнение нового элемента
СозданныйТовар = ЮТЗапрос.Запись("Справочник.Товары", УсловиеПоискаТовара);
ЮТест.ОжидаетЧто(СозданныйТовар, "Данные нового товара")
.Заполнено()
.Свойство("Наименование").Заполнено()
.Свойство("Код").Заполнено()
.Свойство("Артикул").Заполнено();
```
:::tip Проверка заполнения
Не рекомендую использовать `ЮТест.ОжидаетЧтоТаблицаБазы` проверки заполнения созданных/измененных данных.
Лучше сначала получить запись используя `ЮТЗапрос.Запись` и с помощью `ЮТест.ОжидаетЧто` проверить ее заполнение.
Разница будет в тексте ошибки:
* `ЮТест.ОжидаетЧтоТаблицаБазы` скажет, что запись в базе отсутствует, без разницы, был ли создан товар или он был некорректно заполнен.
* `ЮТЗапрос.Запись` + `ЮТест.ОжидаетЧто` позволит отдельно проверить наличе записи в базе и корректность заполнения.
При отсутствии будет выдана ошибка, что данные не заполнены, а при некорректном заполнении сообщит, какое свойство не заполнено.
:::

View File

@ -0,0 +1,66 @@
---
tags: [Начало, Утверждения]
sidebar_position: 1
---
# Утверждения
Тестирование - это проверка системы на **соответствие требованиям**. Из этого следует, что при тестировании, у нас должны быть требования к тестируемой функциональности, сформулированные ожидания.
Для описания ожиданий в тестах, использующих YAxUnit, применяется механизм утверждений, с помощью которых, разработчик описывает требования к тестируемому функционалу.
Утверждения позволяют, не только фиксировать ожидания, но и помогают формировать:
1. более читаемый и простой тест,
2. формируют "дружелюбные" и понятные сообщения об ошибках,
3. прививают единообразие у сокращают количество "велосипедов".
```bsl title="Пример утверждения"
ЮТест.ОжидаетЧто(ДанныеЗаписи)
.Свойство("Поставщик").Равно(ДанныеОбъекта.Поставщик)
.Свойство("Товары")
.ИмеетДлину(1)
.Свойство("Товары[0].НомерСтроки").Равно(2)
.Свойство("Товары[0].Товар").Равно(ДанныеОбъекта.Товары[0].Товар);
```
В результате этой проверки (ошибочной) будет выброшено следующее исключение:
> Ожидали, что проверяемое значение `Поступление товара 000000001 от 12.03.2024 3:06:17` содержит свойство `Товары[0].НомерСтроки`, которое равно `2`, но это не так.
А так оно будет выглядеть в EDT.
![report](images/assertions-report.png)
## Зачем нужны утверждения YAxUnit
Основной задачей утверждений является формирование и проверка наши ожидания.
Например, сложение 2 и 3 должно вернуть нам 5.
Можно перефразировать это требование (ожидание) - тест ожидает, что `2 + 3` равно `5`.
При помощи утверждений это превращается в следующий код - `ЮТест.ОжидаетЧто(2 + 3).Равно(5)`.
Вы можете проверить результат работы и без использования утверждений - `Если 2 + 3 <> 5 Тогда ВызватьИсключение "2 + 3 <> 5";КонецЕсли;`
Но отличии от ручной проверки с использованием условий и исключений утверждения дают ряд преимуществ:
* Всегда, ну или почти, понятные сообщения об ошибках, которые помогают локализовать причину.
* Также вам не надо ломать голову, что написать в исключении.
* Тесты получаются более читаемыми, цепочки вызовов формируют, что-то вроде предложений.
* Не все проверки столько просты как в примере, есть предикаты, сравнение таблиц, проверка вложенных свойств и данных в информационной базе
* Ошибки утверждений помечаются другим типов ошибки, это позволяет понять упал тест (не совпало ожидание) или наш код сломался.
В итоге: утверждений вещь полезная, изучите и используйте ее.
## Возможности
* Проверки [значений](assertions-base.md#доступные-методы)
* Проверки [методов объекта на выброс исключений](assertions-base.md#проверка-выполнения-метода)
* Проверки [данных информационной базы](assertions-db.md)
Это базовые методы проверки, с помощью которых вы можете проверить любое поведение (или почти любое) системы. На их основе вы можете сформировать библиотеку своих "бизнес" утверждений, заточенных под ваше решение, что облегчит вам разработку тестов.
:::tip Рекомендации
* Используйте отступы и форматирование для улучшения читаемости.
* Не забывайте указывать описания для проверок.
* Не стоит писать проверки ради проверок, проверяйте только то, что необходимо для кейса.
Слишком большой код (цепочки утверждений в частности) сложно поддерживать, при необходимости разбейте его на блоки (методы).
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1,7 @@
---
tags: [Начало, Универсальные методы]
sidebar_position: 2
---
# Коллекции

View File

@ -0,0 +1,13 @@
# Дополнительные модули
В дополнение к основным возможностям тестового движка, для упрощения работы при разработке тестов, добавлены следующие модули:
* [`ЮТЗапрос`](queries.md) - предоставляет методы получения данных из информационной базы.
* [`ЮТКоллекции`](collections.md) - универсальные методы для работы с различными коллекциями (массивы, таблицы, структур и тд)
* [`ЮТСтроки`](/api/ЮТСтроки) - универсальные методы для работы со строками.
* [`ЮТЛогирование`](/api/ЮТЛогирование) - методы логирования.
* [`ЮТФабрика`](/api/ЮТФабрика) - методы для создания различных объектов движка, которые могут понадобиться при разработке тестов.
* [`ЮТКомпоненты`](/api/ЮТКомпоненты) - предоставляет доступ к внешним компонентам движка.
* [`ЮТМетаданные`](/api/ЮТМетаданные) - методы для работы с метаданными.
* [`ЮТРегистрацияОшибок`](/api/ЮТРегистрацияОшибок) - методы работы с ошибками.
* [`ЮТИсключения`](/api/ЮТИсключения) - методы для формирования текстов исключений.

View File

@ -0,0 +1,127 @@
---
tags: [Начало, ДанныеИБ]
sidebar_position: 1
---
# Запросы
Для получения данных базы предоставлен API выполнения запросов [`ЮТЗапросы`](/api/ЮТЗапросы).
Позволяет как с сервера, так и с клиента получать данные информационной базы и имеет следующие методы:
* `ЗначенияРеквизитов` - Возвращает значения реквизитов ссылки в виде структуры.
```bsl
ДанныеСсылки = ЮТЗапросы.ЗначенияРеквизитов(Ссылка, "Штрихкод, Поставщик");
ЮТест.ОжидаетЧто(ДанныеСсылки)
.ИмеетТип("Структура")
.Заполнено()
.Свойство("Штрихкод").Равно(Данные.Штрихкод)
.Свойство("Поставщик").Равно(Данные.Поставщик);
```
* `ЗначениеРеквизита` - Возвращает значение реквизита ссылки
```bsl
ЮТест.ОжидаетЧто(ЮТЗапросы.ЗначениеРеквизита(Ссылка, "Наименование"))
.ИмеетТип("Строка")
.Заполнено()
.Равно(Данные.Наименование);
```
* `Запись` - Возвращает первую запись таблицы, соответствующую условиям. Возвращается структура, со всеми данными объекта, включая табличные части.
```bsl
ДанныеЗаписи = ЮТЗапросы.Запись("Документ.ПриходТовара",
ЮТест.Предикат()
.Реквизит("Ссылка").Равно(Ссылка));
ЮТест.ОжидаетЧто(ДанныеЗаписи)
.Свойство("Ссылка").Равно(Ссылка)
.Свойство("Поставщик").Равно(ДанныеОбъекта.Поставщик)
.Свойство("Товары")
.ИмеетТип("Массив")
.ИмеетДлину(1)
.Свойство("Товары[0].НомерСтроки").Равно(1)
.Свойство("Товары[0].Товар")
.Равно(ДанныеОбъекта.Товары[0].Товар);
```
* `Записи` - Возвращает записи таблицы, соответствующую условиям. Возвращается массив структур, со всеми данными объектов, включая табличные части.
```bsl
Записи = ЮТЗапросы.Записи("Справочник.Товары",
ЮТест.Предикат()
.Реквизит("Поставщик").Равно(Поставщик));
ЮТест.ОжидаетЧто(Записи)
.ИмеетТип("Массив")
.ИмеетДлину(5);
Для Каждого Запись Из Записи Цикл
ЮТест.ОжидаетЧто(Запись)
.ИмеетТип("Структура")
.Свойство("Ссылка")
.Свойство("Поставщик").Равно(Поставщик)
.Свойство("Код");
КонецЦикла;
```
* `ЗначенияРеквизитовЗаписи` - Возвращает значения реквизитов первой записи таблицы, соответствующей условиям
```bsl
Предикат = ЮТест.Предикат()
.Реквизит("Штрихкод").Равно(Данные.Штрихкод);
ДанныеСсылки = ЮТЗапросы.ЗначенияРеквизитовЗаписи("Справочник.Товары", Предикат, "Штрихкод, Поставщик, Поставщик.Наименование");
ЮТест.ОжидаетЧто(ДанныеСсылки)
.ИмеетТип("Структура")
.Заполнено()
.Свойство("Штрихкод").Равно(Данные.Штрихкод)
.Свойство("Поставщик").Равно(Данные.Поставщик)
.Свойство("ПоставщикНаименование").Равно(Строка(Данные.Поставщик));
```
* `ЗначениеРеквизитаЗаписи` - Возвращает значение реквизита первой записи таблицы, соответствующей условиям
```bsl
Предикат = ЮТест.Предикат()
.Реквизит("Поставщик").Равно(Данные.Поставщик);
Значение = ЮТЗапросы.ЗначениеРеквизитаЗаписи("Справочник.Товары", Предикат, "Поставщик");
ЮТест.ОжидаетЧто(Значение)
.Равно(Данные.Поставщик);
```
* `ТаблицаСодержитЗаписи` - Вернет признак, содержит ли таблица записи, удовлетворяющие переданным условиям
```bsl
ЮТест.ОжидаетЧто(
ЮТЗапросы.ТаблицаСодержитЗаписи("Справочник.Товары",
ЮТест.Предикат()
.Реквизит("Наименование").Равно(ДанныеСправочника.Наименование)))
.ЭтоИстина();
```
* `РезультатЗапроса` - Возвращает результат выполнения простого запроса, используя описание запроса
```bsl
ОписаниеЗапроса = ЮТЗапросы.ОписаниеЗапроса();
ОписаниеЗапроса.ИмяТаблицы = "Справочник.Товары";
ОписаниеЗапроса.Условия.Добавить("Ссылка = &Ссылка");
ОписаниеЗапроса.Условия.Добавить("НЕ ПометкаУдаления");
ОписаниеЗапроса.ЗначенияПараметров.Вставить("Ссылка", Товар);
ОписаниеЗапроса.ВыбираемыеПоля.Добавить("Ссылка");
ОписаниеЗапроса.ВыбираемыеПоля.Добавить("1+1 КАК Число");
ЮТест.ОжидаетЧто(ЮТЗапросы.РезультатЗапроса(ОписаниеЗапроса))
.ИмеетДлину(1)
.Свойство("[0].Ссылка").Равно(Товар)
.Свойство("[0].Число").Равно(2);
```
* `РезультатПустой` - Определяет, есть ли в результате записи, используя описание запроса
```bsl
ОписаниеЗапроса = ЮТЗапросы.ОписаниеЗапроса();
ОписаниеЗапроса.ИмяТаблицы = "Справочник.Товары";
ОписаниеЗапроса.Условия.Добавить("Ссылка = &Ссылка");
ОписаниеЗапроса.ЗначенияПараметров.Вставить("Ссылка", Ссылка);
ЮТест.ОжидаетЧто(ЮТЗапросы.РезультатПустой(ОписаниеЗапроса)).ЭтоЛожь();
```
* `ОписаниеЗапроса` - Формирует описание простого запроса
```bsl
ОписаниеЗапроса = ЮТЗапросы.ОписаниеЗапроса();
ОписаниеЗапроса.ИмяТаблицы = "Справочник.Товары";
ОписаниеЗапроса.Условия.Добавить("Ссылка = &Ссылка");
ОписаниеЗапроса.Условия.Добавить("НЕ ПометкаУдаления");
ОписаниеЗапроса.ЗначенияПараметров.Вставить("Ссылка", Товар);
ОписаниеЗапроса.ВыбираемыеПоля.Добавить("Ссылка");
ОписаниеЗапроса.ВыбираемыеПоля.Добавить("1+1 КАК Число");
```

View File

@ -0,0 +1,72 @@
---
tags: [Начало, Контекст]
sidebar_position: 5
---
# Контекст
Механизм глобальных контекстов в тестовом движке играет ключевую роль. Он предоставляет следующие основные возможности:
1. Передача данных между тестами. Если вам нужно использовать определенные данные в нескольких тестах, вы можете передать их с помощью контекстов.
2. Работа с [текучими выражениями](/docs/getting-started/fluent-api.md). Контексты обеспечивают поддержку текучих выражений, что упрощает написание и чтение кода тестов.
3. [Удаление](test-data/test-data-deletion.md) тестовых данных. После выполнения тестов вы можете использовать контексты для очистки тестовых данных, которые больше не нужны.
4. Передача настроек [Мокито](mocking/mockito.md). Это позволяет настроить поведение мокируемого метода прямо из теста, что упрощает процесс тестирования.
Однако стоит отметить, что механизм контекстов имеет некоторые ограничения.
* Он не синхронизируется между клиентом и сервером.
* Контексты тестов имеют ограниченное время жизни. Например, контекст теста "живет" только в рамках теста и [событиях](events.md) `ПередКаждымТестом`, `ПослеКаждогоТеста`.
## Использование контекстов в тестах
При разработке тестов, вам могут пригодиться следующие контексты:
1. Контекст теста ([ЮТест.КонтекстТеста](/api/ЮТест#контексттеста)). Это `структура`, которая существует в рамках одного теста. Она доступна в каждом тесте и в обработчиках [событий](events.md) `ПередКаждымТестом` и `ПослеКаждогоТеста`, а также в аналогичных переопределенных обработчиках. Это означает, что вы можете использовать этот контекст для хранения и доступа к данным, которые нужны только в рамках одного теста.
2. Контекст тестового набора ([ЮТест.КонтекстТестовогоНабора](/api/ЮТест#контексттестовогонабора)). Это `структура`, которая существует в рамках набора тестов. Она доступна в каждом тесте набора и в обработчиках [событий](events.md) `ПередТестовымНабором`, `ПослеТестовогоНабора`, `ПередКаждымТестом` и `ПослеКаждогоТеста`, а также в аналогичных переопределенных обработчиках. Это означает, что вы можете использовать этот контекст для хранения и доступа к данным, которые нужны в рамках всего набора тестов.
3. Контекст тестового модуля ([ЮТест.КонтекстМодуля](/api/ЮТест#контекстмодуля)). Это `структура`, которая существует в рамках тестового модуля. Она доступна в каждом тесте модуля и в обработчиках [событий](events.md) `ПередВсемиТестами`, `ПослеВсехТестов`, `ПередТестовымНабором`, `ПослеТестовогоНабора`, `ПередКаждымТестом` и `ПослеКаждогоТеста`, а также в аналогичных переопределенных обработчиках. Это означает, что вы можете использовать этот контекст для хранения и доступа к данным, которые нужны в рамках всего тестового модуля.
4. Контекст ([ЮТест.Контекст](/api/ЮТест#контекст)): Это умный контекст, который облегчает работу с различными тестовыми контекстами. Он имеет два метода: `Значение` и `УстановитьЗначение`.
Метод `Значение` последовательно ищет необходимое значение во всех контекстах (теста, набора, модуля) и возвращает первое найденное.
Метод `УстановитьЗначение` автоматически определяет контекст, в котором вызывается установка значения, и сохраняет значение в соответствующем контексте. Это позволяет вам не задумываться о том, в каком контексте нужно сохранить данные, и при необходимости переопределять значения.
```bsl
// Создаем организацию, которую будем использовать во всех тестах
// Но для конкретного набора переопределим ее
// При этом используется общий метод создания данных, который получает организацию из контекста
Процедура ПередВсемиТестами()
ЮТест.Контекст().УстановитьЗначение("Организация", НоваяОрганизация());
КонецПроцедуры
Процедура ПередТестовымНаборомДругойОрганизации()
ЮТест.Контекст().УстановитьЗначение("Организация", НоваяДругаяОрганизация()); // Переопределим настройку из модуля
КонецПроцедуры
Процедура Тест()
Документ = СоздатьДокумент();
...
КонецПроцедуры
Процедура Тест2()
ЮТест.Контекст().УстановитьЗначение("Организация", НоваяТретьяОрганизация()); // Переопределим настройку
Документ = СоздатьДокумент();
...
КонецПроцедуры
Функция СоздатьДокумент()
Документ = НовыйДокумент();
Документ.Организация = ЮТест.Контекст().Значение("Организация");
КонецФункции
```
## Реализация
Реализация механизма контекстов включает следующие аспекты:
* Все контексты сохраняются в глобальной структуре. Это означает, что они доступны во всем приложении и могут быть использованы в различных частях кода.
* На клиентской стороне эта глобальная структура представлена в виде переменной модуля приложения.
* На серверной стороне эта структура сохраняется во временном хранилище. Адрес этого хранилища сохраняется в настройках пользователя, а также кэшируется модулем повторного использования.
* Для каждой задачи (например, контекст текучих выражений или настройки мокито) в этой структуре создается свой предопределенный элемент.
* Методы работы с контекстами унифицированы и собраны в модуле ЮТКонтекстСлужебный. Однако не рекомендуется использовать их непосредственно в тестах.
* У каждого контекста может быть свой собственный жизненный цикл, который контролируется конкретным механизмом движка. Это позволяет гибко управлять временем жизни контекстов в зависимости от требований решаемой задачи.

View File

@ -0,0 +1,69 @@
---
tags: [Начало, События]
sidebar_position: 6
---
# События
## События тестов
События тестов можно использовать для настройки тестового окружения:
* Установка констант и других настроек.
* Создание тестовых данных.
* Удаление созданных данных.
* Если результат обработки события необходимо передать в тест, то это можно сделать с помощью [контекста](./context.md)
Вот некоторые из событий, которые могут быть интересны разработчикам:
* `ПередВсемиТестами` - Это событие вызывается перед прогоном тестов модуля в каждом контексте (`&НаСервер` и `&НаКлиенте` для клиент-серверного модуля). Это может быть полезно для установки общих настроек или данных, которые будут использоваться всеми тестами.
* `ПередТестовымНабором` - Это событие вызывается перед прогоном тестов набора. Это может быть полезно для установки или переопределения для тестов набора настроек или данных.
* `ПередКаждымТестом` - Это событие вызывается перед прогоном каждого теста. Это может быть полезно для установки специфических для теста настроек или данных.
* `ПослеКаждогоТеста` - Это событие вызывается после прогона каждого теста. Это может быть полезно для очистки данных или настроек, установленных перед тестом.
* `ПослеТестовогоНабора` - Это событие вызывается после прогона всех тестов набора.
* `ПослеВсехТестов` - Это событие вызывается после прогона всех тестов модуля для контекста (после всех клиентских, либо после всех серверных).
:::info Транзакция
При использовании транзакций ([ЮТТесты.ВТранзакции()](/api/ЮТТесты#втранзакции)) события `ПередКаждымТестом` и `ПослеКаждогоТеста` включаются в транзакцию. Другие события выполняются вне транзакции.
:::
Для обработки этих событий необходимо в тестовом модуле разместить одноименный экспортный метод без параметров.
:::warning[Обработчики могут вызываться дважды]
Важной особенностью всех событий, связанных с исполнением тестов, это то что они могут вызываться дважды - для клиента и для сервера.
Если у нас есть клиент-серверный модуль с тестами доступными и на клиенте, и на сервер, то события будет вызвано дважды - и перед запуском клиентских, и перед запуском серверных тесов. Это касается всех событий, и события `ПередВсемиТестами`, и события `ПослеКаждогоТеста`
:::
При необходимости разработчик может переопределить обработчики событий при регистрации тестов, вызвав `Перед` и/или `После` и указав имя нужного метода.
```bsl
Процедура ИсполняемыеСценарии() Экспорт
ЮТТесты
.Перед("ПодготовитьДанныеДляТестов") // Переопределение обработчика выполняемого перед прогоном тестов модуля
.После("ОчиститьДанныеТестов") // Переопределение обработчика выполняемого после прогона тестов модуля
.ДобавитьТестовыйНабор("МойНаборТестов")
.ДобавитьТест("МойПервыйТест")
.ДобавитьТест("МойВторойТест")
.Перед("ПодготовитьДанныеДляПервогоТеста") // Переопределение обработчика для конкретного теста
.ДобавитьТестовыйНабор("МойДругойНаборТестов")
.Перед("ПодготовитьДанныеДругогоНабора") // Переопределение обработчика вызываемого перед прогоном набора тестов
.ДобавитьТест("МойТретийТест")
КонецПроцедуры
```
## События в движке
YAxUnit также использует события для работы внутренних механизмов:
* Управление жизненным циклом [контекстов](context.md)
* [Очистка тестовых данных](test-data/test-data-deletion.md)
* Логирование
* и другие.
Благодаря событиям вы можете развивать и адаптировать механизмы движка под себя, например:
* Выполнять начальную подготовку базы или настройку движка перед прогонами
* Реализовывать свои механизмы (например удаление данных с использованием подписок)
* Формировать отчеты о тестировании online.

View File

@ -1,6 +1,136 @@
---
sidebar_position: 0
tags: [Начало]
sidebar_label: Руководство
sidebar_position: 1
---
# Возможности
# Возможности YAxUnit
```mermaid
mindmap
t((Тест))
(Регистрация)
Регистрация и контекст исполнения
Настройка
Параметры вызова
Обработчики событий
Очистка данных
(Мокирование)
Методы конфигурации
HTTP-запрос
ADO.RecordSet
(Создание данных ИБ)
Конструктор объекта
Создание пустышек
Загрузка из макета
(Генерация данных)
Генерация случайных данных
Генерация правдоподобных данных
(Проверка)
Базовые утверждения
Утверждения проверка данных ИБ
Проверка статистики вызовов
Предикаты
(Отчеты)
JUnit
Allure
(Удобства)
Плагин для EDT
Форма настройки/запуска тестов
Текучие выражения
```
YAxUnit предоставляет относительно богатый набор инструментов для помощи разработчикам в написании тестов.
## Регистрация и настройка выполнения тестов
[Описание](test-registration.md), [API](/api/ЮТТесты)
Создание тестов начинается с регистрации их в движке, необходимо рассказать движку, какие имеются тесты, как их запускать, с какими параметрами.
Для этого используется обязательный предопределенный экспортный метод `ИсполняемыеСценарии`
```bsl
Процедура ИсполняемыеСценарии() Экспорт
ЮТТесты.Добавить...
КонецПроцедуры
```
Подробнее смотрите в [описании](test-registration.md).
## Реализация тестов
Большая часть методов программного интерфейса YAxUnit собрана в модуле [ЮТест](/api/ЮТест). Этот модуль является точкой входа в API используемый в тестах, он предоставляет доступ к другим модулям движка:
* [`ЮТест.Данные()`](/api/ЮТТестовыеДанные) - Возвращает модуль работы с [тестовыми данными](test-data/)
* [`ЮТест.ОжидаетЧто()`](/api/ЮТУтверждения) - Возвращает модуль работы с [утверждениями](assertions/assertions-base.md)
* [`ЮТест.ОжидаетЧтоТаблицаБазы()`](/api/ЮТУтвержденияИБ) - Возвращает модуль работы с [утверждениями для проверки объектов информационной базы](assertions/assertions-db.md)
* [`ЮТест.Контекст()`](/api/ЮТКонтекстТеста) - Возвращает модуль работы с [контекстом](context.md)
* [`ЮТест.Предикат()`](/api/ЮТПредикаты) - Возвращает конструктор [предикатов](predicates.md)
* [`ЮТест.Варианты()`](/api/ЮТКонструкторВариантов) - Возвращает конструктор вариантов прогона теста.
### Создание тестовых данных
[Описание](test-data/), [API](/api/ЮТТестовыеДанные)
Почти все тесты опираются на данные, будь то параметры вызова или записи информационной базы. Вам придется эти данные создавать.
Настоятельно рекомендую создавать данные программно, не загрузкой из макетов или вручную, а именно программно генерировать. О причинах можете почитать в [статье](test-data/) о тестовых данных
Движок предоставляет различные инструменты для создания данных:
* [`ЮТест.Данные().КонструкторОбъекта`](/api/ЮТТестовыеДанные#конструкторобъекта), с помощью которого вы можете создать объект информационной базы с необходимыми реквизитами.
* Создание пустышек - примитивных объектов с минимальным заполнения.
* [`ЮТест.Данные().СоздатьЭлемент`](/api/ЮТТестовыеДанные#создатьэлемент)
* [`ЮТест.Данные().СоздатьДокумент`](/api/ЮТТестовыеДанные#создатьдокумент)
* [`ЮТест.Данные().СоздатьГруппу`](/api/ЮТТестовыеДанные#создатьгруппу)
* Возможна загрузка из макета. Вы можете создать все необходимые данные [загрузив их из макета](test-data/load-from-templates.md)
* Работа с временными файлами.
* [Подражатель](/api/ЮТПодражатель), для генерации правдоподобных данных.
* Генерация случайных значений.
* [`ЮТест.Данные().КонструкторОбъектаXDTO`](/api/ЮТТестовыеДанные#конструкторобъектаxdto), с помощью которого вы можете создать XDTO объект.
### Мокирование
[Описание](mocking), [API](/api/Мокито)
При тестировании полезно минимизировать "внешнее" воздействие на проверяемый функционал. Например, не использовать внешний http сервис, а эмулировать взаимодействие с ним, либо вместо выполнения запроса к базе данных вернуть предопределенный результат или вместо документа в качестве параметра передать структуру.
Те заменить какой-либо объект обманкой, упрощенной его версией.
* [`Мокито`](mocking/mockito.md) позволяет настроить/изменить поведение нужных методов конфигурации
* [`Вернуть`](/api/МокитоОбучение#вернуть) нужный результат для определенных параметров.
* [`ВыброситьИсключение`](/api/МокитоОбучение#выброситьисключение) при в нужный момент.
* [`Пропустить`](/api/МокитоОбучение#пропустить) метод (не выполнять).
* Просто [`Наблюдать`](/api/МокитоОбучение#наблюдать) (собирать статистику вызовов) за методом.
* За настройку условий срабатывания тех или иных действий отвечает метода [`Когда`](/api/МокитоОбучение#когда)
* [`ЮТест.Данные().HTTPСервисЗапрос()`](/api/ЮТТестовыеДанные#httpсервисзапрос) позволяет настроить и эмулировать входящий запрос http-сервиса.
Если логика обработки запросов вынесена в отдельный общий модуль, то вы с легкостью с его помощью сможете протестировать ваш сервис без использования веб-сервера.
* [`ЮТест.Данные().ADORecordSet()`](/api/ЮТТестовыеДанные#adorecordset) позволяет протестировать "прямые" запросы к SQL.
### Проверка ожиданий
[Описание](assertions/)
* Проверка результата работы функции. Для проверки переменных (результатов методов) реализованы [базовые утверждения](assertions/assertions-base), подробнее со списком возможностей можете ознакомиться в [сгенерированном описании api](/api/ЮТУтверждения).
* Если метод должен изменить данны в информационной базе, то эти изменения можно проверить с помощью [утверждений ИБ](assertions/assertions-db), [описании api утверждений](/api/ЮТУтвержденияИБ).
* В некоторых случаях необходимо проанализировать какие методы и с какими параметрами вызывались. Здесь вам поможет [проверка статистики вызовов](/api/МокитоПроверки#вызовы), который собирает [Мокито](mocking)
### Методы-помощники
* Изменение данных в базе
* [`ЮТест.Данные().УстановитьЗначениеРеквизита`](/api/ЮТТестовыеДанные#установитьзначениереквизита)
* [`ЮТест.Данные().УстановитьЗначенияРеквизитов`](/api/ЮТТестовыеДанные#установитьзначенияреквизитов)
* [Предикаты](predicates.md)
* [Получение данных из базы и работа с запросами](auxiliary-modules/queries.md)
* Варианты вызова. Чтобы повысить покрытие метода тестом можно сформировать наборы (варианты) параметров вызова метода. Для упрощения работы с ними вы можете воспользоваться методом [`ЮТест.Варианты()`](/api/ЮТест#варианты)
* [`ЮТест.Пропустить`](/api/ЮТест#пропустить)
* [`ЮТест.Пауза`](/api/ЮТест#пауза)
* [`ЮТест.ВывестиВКонсоль`](/api/ЮТест#вывестивконсоль)
## Примеры
При возникновении трудностей с написанием тестов вы всегда можете
* Посмотреть примеры [тестов движка](https://github.com/bia-technologies/yaxunit/tree/develop/tests/src/CommonModules)
* Тесты других открытых проектов
* [bellerage-ssl](https://github.com/Bellerage-IT/bellerage-ssl/tree/master/src/cfe/yaxunit/src/CommonModules)

View File

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 135 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View File

@ -0,0 +1,154 @@
---
tags: [Начало, Мокирование]
sidebar_position: 3
---
# Мокирование
## Вводная
Мокирование - это инструмент/подход для изоляции тестируемых частей кода и контроля над зависимостями.
При мокировании, мы вместо реальных (продуктовых) объектов подсовываем моки - подделки, фальшивки. Это "двойники" реальных объектов.
Например:
* Вместо, реального почтового сервера мы можем поднять в докере легковесный smtp сервер и настроить систему на взаимодействие с ним
* Используя SOAP UI/Postman/Wiremock или что-то другое мы сможем протестировать систему без использования реального сервиса
* Не редко вместо поднятия промышленной СУБД в тестах используются легковесные встраиваемые СУБД (sql lite, h2)
Все это относится к мокированию и позволяет упростить процесс поднятия окружения для тестов.
Но также моки могут быть использованы непосредственно при тестировании, например вместо реального документа в тестируемый метод вы можете передать структуру, содержащую все нужные реквизиты (Dummy, болванка), либо обработку с нужными методами (Мок), либо что-то иное, что позволяет вам упростить тест.
Например, при тестировании http-сервисов передаем мок, который "притворяется" запросом, реализует интерфейс объекта `HTTPСервисЗапрос`.
Основное ограничение такого подхода - обработки и структуры должны передаваться в качестве параметров в тестируемый метод. Если нам очень необходимо подменить объект, который вычисляется внутри метода, то приходится попотеть, либо передавать его в качестве доп. параметров и закладывать работы с ним в конфигурации, либо с помощью расширения возвращать жестко закодированное значение. Каждый раз, в таких случаях, приходится что-то изобретать и выдумывать способ внедрения моков.
## Мокито
Для унификации подхода к работе с моками мы разработали инструмент [Мокито](mockito.md), который облегчает тестирование.
Он предоставляет
* подход к внедрению моков
* программный интерфейс для управления моками.
Например, у нас есть метод отправки СМС, через шлюз.
```mermaid
sequenceDiagram
autonumber
actor РоботОтправки
participant ОтправкаСМС
participant ДрайверОтправки as ДрайверОтправки <br/>ОтправкаСМС_Провайдер1
participant Шлюз as СМС шлюз
РоботОтправки->>+ОтправкаСМС: Отправить СМС
ОтправкаСМС->>ОтправкаСМС: Определение драйвера
ОтправкаСМС->>ОтправкаСМС: Получение СМС к отправке
ОтправкаСМС->>+ДрайверОтправки: Отправить пакет сообщений
loop Отправка сообщений
ДрайверОтправки-->>ДрайверОтправки: Подготовка тела запроса
critical Запрос к внешнему ресурсу
ДрайверОтправки->>Шлюз: Запрос на отправку сообщения
Шлюз-->>ДрайверОтправки: Статус отправки
end
ДрайверОтправки->>ДрайверОтправки: Анализ статуса
end
ДрайверОтправки-->>-ОтправкаСМС: Статус отправки сообщений
ОтправкаСМС-->>-РоботОтправки: Статус отправки
```
Он использует запросы к внешнему ресурсу, СМС шлюзу, который необходим в текущей схеме. Для решения этой проблемы мы можем:
1. Отправлять реальные СМС в тестах
2. Использовать тестовый шлюза, если провайдер предоставляет такую возможность
3. Поднять мокирующий web-server
4. Заложить в код обходы для тестирования
5. Воспользоваться Мокито, написав в тесте:
```bsl
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(СериализацияJSON.ЗначениеВСтроку(Новый Структура("id, status",
"9999",
"delivered")));
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("ПослатьСообщение") // Теперь, вместо реального запроса к шлюзу
.Вернуть(Ответ) // метод всегда будет возвращать нужный нам ответ
.Прогон();
РоботОтправки.ОтправкаСМС();
```
В это случае, при отправке, не будут выполнятся запросы к шлюзу, вместо этого метод `ПослатьСообщение` вернет предопределенный результат.
Подробнее о возможностях и принципах работы с Мокито, вы можете почитать в отдельной [статье](mockito.md).
## Моки
Мокито помогает изменять логику работы системы для изоляции тестируемого метода и уменьшения влияния других компонентов системы.
Он закрывает множество задач, но иногда возникают задачи когда даже он не может помочь.
### `HTTPСервисЗапрос`
Первый пример с которым мы столкнулись - тестирование http-сервисов. Сервис на вход принимает объект `HTTPСервисЗапрос`, который невозможно создать и настроить из кода 1С.
Необходимо было либо публиковать решение на web-сервере, либо дорабатывать код, чтобы он принимал структуры.
Несколько первых тестов были созданы с помощью структур. Позже возникла идея реализовать мок, который бы походил на `HTTPСервисЗапрос`, реализовывал его интерфейс, и который можно настроить из теста.
С ним тестировать сервисы стало намного проще и удобнее, позже его включили в YAxUnit, [`ЮТест.Данные().HTTPСервисЗапрос`](/api/ЮТТестовыеДанные#httpсервисзапрос)
Возможности:
* Реализует методы `HTTPСервисЗапрос`
* Свойства
* `HTTPМетод` \ `HTTPMethod`
* `БазовыйURL` \ `BaseURL`
* `Заголовки` \ `Headers`
* `ОтносительныйURL` \ `RelativeURL`
* `ПараметрыURL` \ `URLParameters`
* `ПараметрыЗапроса` \ `QueryOptions`
* Методы
* `ПолучитьТелоКакДвоичныеДанные()` \ `GetBodyAsBinaryData()`
* `ПолучитьТелоКакПоток()` \ `GetBodyAsStream()`
* `ПолучитьТелоКакСтроку()` \ `GetBodyAsString()`
* Имеет методы настройки, реализованные в виде [текучих выражений](/docs/getting-started/fluent-api.md)
* Установка тела
* `УстановитьТелоКакДвоичныеДанные()`
* `УстановитьТелоКакСтроку()`
* `УстановитьТелоКакСтрокуJSON()`
* Методы установки свойств
* `ДобавитьЗаголовок()`
* `ДобавитьПараметрЗапроса()`
* `ДобавитьПараметрURL()`
* `Метод()`
* `БазовыйURL()`
* `ОтносительныйURL()`
### `ADO.RecordSet`
При тестировании прямых запросов к СУБД также возникает потребность использовать мок, для эмуляции чтения из `ADO.RecordSet`.
Для таких кейсов был добавлен [`ЮТест.Данные().ADORecordSet`](/api/ЮТТестовыеДанные#adorecordset) реализующий интерфейс `ADO.RecordSet`
* Свойства
* `EOF`
* `BOF`
* `AbsolutePosition`
* `RecordCount`
* `Fields`
* Методы
* `MoveFirst()`
* `MoveNext()`
* `Close()`
* `Fields()`
* `EOF()`
* `BOF()`
* Методы настройки, реализованные в виде [текучих выражений](/docs/getting-started/fluent-api.md)
* `ЮТест.Данные().ADORecordSet` - принимает информацию о колонках
* `Добавить()` - добавляет новую строку значений
### Создание своих моков
При возникновении подобных ситуаций, когда для тестов вам нужны объекты с какой-то логикой/методами, по аналогии с примерами выше вы без проблем сможете создать нужные вам моки.
Это очень сильно облегчает тестирование и позволяет вам избежать лишнего вмешательства в тестируемый код.

View File

@ -0,0 +1,713 @@
---
tags: [Начало, Мокирование]
sidebar_position: 2
---
# Мокито
[Мокито](/api/Мокито) - модуль созданный по образу популярного java-фреймворка для тестирования [Mockito](https://site.mockito.org/). Расширяет возможности тестирования, позволяет легко менять логику работы системы подменяя результаты работы методов, отключая какие-либо алгоритмы и проверки.
## Возможности
С помощью Мокито мы можем с легкостью управлять поведением системы прямо из теста, для этого есть возможность:
* Настраивать какие методы "меняют" свое поведение, а какие обрабатывают "как положено".
* Для разных тестов или кейсов внутри теста можно перенастраивать поведение методов.
* Задавать условия на параметры метода.
* Мокировать как экспортные так и приватные методы, процедуры и функции.
* Настраивать разные "реакции" на вызов метода:
* Вернуть указанный результат
* Вызвать исключение
* Не выполнять метода
* Просто наблюдать за методом (собирать статистику по вызовам)
Вы можете мокировать почти любой метод конфигурации, **исключение** - методы глобального контекста.
1. Методы общих модулей
2. Методы менеджеров
3. Методы конкретных объектов, будь то документы или справочники
Примеры:
* Для всех объектов определенного типа - обучаете через менеджер
```bsl
Мокито.Обучение(Документы.ПриходТовара) // Для всех документов ПриходТовара
.Когда("ОбработкаПроведения")
```
* Если нужно мокировать метод экземпляра объекта, то указываем его при обучении
```bsl
Мокито.Обучение(ДокументОбъект) // Для конкретного экземпляра объекта,
.Когда("ОбработкаПроведения") // другой документ объект, даже полученный из той же ссылки отработает без мокирования
```
* либо ссылку на этот объект
```bsl
Мокито.Обучение(ДокументСсылка) // Любой документ объект с той же ссылкой
.Когда("ОбработкаПроведения")
```
* Можно мокировать методы обработок и отчетов
```bsl
Мокито.Обучение(Отчеты.ОСВ) // Для всех отчетов
.Когда("СформироватьОтчет")
Отчет = Отчеты.ОСВ.СоздатьОтчет();
Мокито.Обучение(Отчет) // для конкретного экземпляра отчета
.Когда("СформироватьОтчет")
```
Мокито не может:
* Выполнять произвольную логику при вызове - выполнить алгоритм или вызвать какой-либо другой метод.
* Не может изменять параметры.
* При сборе статистики не хранит результат.
## Принцип работы
Мокито базируется на двух вещах:
* Расширения и заимствование методов, благодаря им появилась возможность менять поведение системы.
* Глобальный контекст, доступный как из теста, так и из заимствованных методов конфигурации.
### Настройка мокируемых методов
Для того чтобы вы смогли мокировать метода, вам в первую очередь необходимо добавить его в свое расширение.
И добавить реализацию по шаблону для функции:
```bsl
&Вместо("<ИмяМокируемогоМетода>")
Функция Мок_<ИмяМокируемогоМетода>(<ПараметрыМетода>)
ПараметрыМетода = Мокито.МассивПараметров(<ПараметрыМетода>);
ПрерватьВыполнение = Ложь;
Результат = МокитоПерехват.АнализВызова(<МокируемыйОбъект>, "<ИмяМокируемогоМетода>", ПараметрыМетода, ПрерватьВыполнение);
Если Не ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(<ПараметрыМетода>);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции
```
Либо для процедуры:
```bsl
&Вместо("<ИмяМокируемогоМетода>")
Процедура Мок_<ИмяМокируемогоМетода>(<ПараметрыМетода>)
ПараметрыМетода = Мокито.МассивПараметров(<ПараметрыМетода>);
ПрерватьВыполнение = Ложь;
МокитоПерехват.АнализВызова(<МокируемыйОбъект>, "<ИмяМокируемогоМетода>", ПараметрыМетода, ПрерватьВыполнение);
Если Не ПрерватьВыполнение Тогда
ПродолжитьВызов(<ПараметрыМетода>);
КонецЕсли;
КонецПроцедуры
```
:::tip
Если метод не содержит параметров можно использовать пустой массив:
```bsl
ПараметрыМетода = Новый Массив;
```
:::
Если вы используете 1С:Enterprise Development Tools (EDT), то можете воспользоваться контекстной командой `1С:Модульные тесты (YAxUnit)` -> `Создать/обновить мок для...`
![edt-create-mock](../images/edt-create-mock.png)
Примеры добавления методов в расширение.
#### Метод общего модуля
Добавляем обработку метода `ПолучитьПрофиль` общего модуля `РаботаСПочтой`
```bsl title="tests\ОбщиеМодули\РаботаСПочтой\Модуль.bsl"
&Вместо("ПолучитьПрофиль")
Функция Мок_ПолучитьПрофиль(ИмяОтправителя, ИспользоватьIMAP) Экспорт
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(ИмяОтправителя, ИспользоватьIMAP);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = МокитоПерехват.АнализВызова(РаботаСПочтой, "ПолучитьПрофиль", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(ИмяОтправителя, ИспользоватьIMAP);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции
```
#### Метод модуля менеджера
Подходит для любого модуля менеджера, будь то, документ, регистр или отчет.
Добавляем обработку метода `НовыйИдентификаторПодписчикаУведомлений` модуля менеджера справочника `Справочники.МобильныеУстройства`
```bsl title="tests\Справочники\МобильныеУстройства\МодульМенеджера.bsl"
&Вместо("НовыйИдентификаторПодписчикаУведомлений")
Процедура Мок_НовыйИдентификаторПодписчикаУведомлений(Подписчик, Идентификатор) Экспорт
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(Подписчик, Идентификатор);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
МокитоПерехват.АнализВызова(Справочники.МобильныеУстройства, "НовыйИдентификаторПодписчикаУведомлений", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Подписчик, Идентификатор);
КонецЕсли;
КонецПроцедуры
```
#### Метод модуля объекта
Добавляем обработку приватного метода `ОбработкаПроведения` модуля объекта справочника `Документ.ПриходТовара`
```bsl title="tests\Документы\ПриходТовара\МодульОбъекта.bsl"
&Вместо("ОбработкаПроведения")
Процедура Мок_ОбработкаПроведения(Отказ, Режим)
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(Отказ, Режим);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
МокитоПерехват.АнализВызова(ЭтотОбъект, "ОбработкаПроведения", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Отказ, Режим);
КонецЕсли;
КонецПроцедуры
```
После этого, вы может управлять поведение метода из ваших тестов.
Тестирование с использованием Мокито можно разделить на 3 стадии:
```mermaid
flowchart LR
training(1. Обучение) --> run(2. Прогон, запуск тестового метода)
run --> check(3. Проверка)
```
1. Сначала вы проводите **обучение** - настраиваете, какие методы мокируем и как они должны себя вести.
2. Потом выполняете тестовый **прогон** целевого метода.
3. После вам может понадобиться **проверка** вызовов, были ли вызваны нужные метода, с какими параметрами.
```bsl
// Подготовка
Ответ = Новый HTTPСервисОтвет(1);
Ответ.УстановитьТелоИзСтроки(СериализацияJSON.ЗначениеВСтроку(Новый Структура("id, status",
"9999",
"delivered")));
// Обучение мокито
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("УстановитьСоединение").Вернуть(Истина)
.Когда("ПослатьСообщение").Вернуть(Ответ)
.Прогон();
// Тестовый прогон
РоботОтправки.ОтправкаСМС();
// Проверка статистики
Мокито.Проверить(ОтправкаСМС_Провайдер1)
.КоличествоВызовов("ПослатьСообщение")
.Равно(1);
```
Подробнее расскажу по каждой стадии
### Обучение
Обучение - это процесс настройки мокито. Вы указываете какие методы и при каких условиях (параметрах) должны изменить свое поведение.
С помощью API вы можете:
* [`Наблюдать()`](/api/МокитоОбучение#наблюдать) - просто наблюдать за методом, собирать статистику.
* [`Когда()`](/api/МокитоОбучение#когда) - указать какой метод и с каким параметрами необходимо обрабатывать.
* [`Вернуть()`](/api/МокитоОбучение#вернуть) - настроить, чтобы метод возвращал нужный результат.
* [`ВыброситьИсключение()`](/api/МокитоОбучение#выброситьисключение) - настроить, чтобы метод выбрасывал исключение.
* [`Пропустить()`](/api/МокитоОбучение#пропустить) - настроить, чтобы метод был пропущен, не выполнился.
* [`ВыполнитьМетод()`](/api/МокитоОбучение#выполнитьметод) - настроить, чтобы метод выполнился как есть.
```bsl
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("УстановитьСоединение").Пропустить()
.Когда("ПослатьСообщение").Вернуть(Ответ)
.Наблюдать("ОбработатьОтвет")
```
Подробнее о методах ниже, а сейчас для лучшего понимания расскажу как работает обучение.
Используя методы API вы формируете настройки `Мокито`, которые при вызове каждого метода обучения изменяются и сохраняются в [глобальном контексте](../context.md) движка. Каждый метод просто сохраняет нужным образом параметры в глобальные структуру.
```mermaid
sequenceDiagram
autonumber
actor Тест
participant Мокито
participant Контекст
Тест-->Мокито: Мокито.Обучение(). Включить обучение.
loop Настройка мокирования методов конфигурации
Тест->>+Мокито: Когда(). Указываем условия перехвата.
Мокито-->>Контекст: Сохранение условий перехвата
Тест->>Мокито: ВернутьРезультат(). Настраиваем поведение.
Мокито-->>Контекст: Привязка поведения к условиям перехвата
Мокито->>-Тест: Завершили настройку перехвата
end
```
:::caution Сброс старых настроек
Каждый вызов метода `Мокито.Обучение`, по умолчанию, очищает предыдущие настройки по указанному объекту. Это нужно учитывать когда вы доучиваете Мокито в тесте.
Если вам не нужно сбрасывать старые настройки по объекту передайте соответствующий параметр
```bsl
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("УстановитьСоединение").Пропустить()
.Когда("ПослатьСообщение").Вернуть(Ответ)
.Наблюдать("ОбработатьОтвет")
.Прогон();
// Какая то логика
Мокито.Обучение(ОтправкаСМС_Провайдер1, Ложь) // Дообучение
.Когда("ПослатьСообщение").Вернуть(Ответ2);
```
:::
В процессе обучения мы создаем правила, описывающие как будет вести себя метод при различных вариантах вызова.
Правила состоят из двух частей
* условие срабатывания (об/ект, метод, параметры), задаются в методе `Когда` или `Наблюдать`
* действие выполняемое при соблюдении условий - `Вернуть`, `ВыброситьИсключение`, `Пропустить`, `ВыполнитьМетод`
#### Условия
Условие состоит из трех частей
* Объект, которому относится метод
* Имя метода
* Параметры метода
##### Объект
Объект является обязательным и задается в методе [`Мокито.Обучение()`](/api/Мокито#обучение).
В качестве объекта могут выступать:
* **Менеджер объекта метаданных**, в таком случае под условие попадают все обращения к этому типу метаданных, будь вызовы методов модуля менеджера или объекта.
* **Ссылка**, под условие попадают все обращения к объектам имеющим ту же ссылку.
* **Объект** - конкретный объект метаданных (документ, обработка, набор записей), только обращения к методам этого экземпляра объекта.
##### Имя метода
Имя метода, является обязательным. Указывается в методах `Когда` или `Наблюдать`.
Можно обрабатывать и экспортные и приватные метода, главное чтобы для него была [выполнена настройка](#настройка-мокируемых-методов)
##### Параметры
Указывается в методах `Когда` или `Наблюдать`, передачей массива ожидаемых параметров.
Необязательная часть условия, если не указывать, но будут обрабатывать все вызовы.
Также необязательно указывать все параметры метода, достаточно указать только первую часть.
В качестве параметров можно указывать:
* Конкретные значения параметров
* Маски
* [`Мокито.ЛюбойПараметр()`](/api/Мокито#любойпараметр) - параметр может принимать любое значение
* [`Мокито.СтроковыйПараметр()`](/api/Мокито#строковыйпараметр) - параметр может принимать только строковые значения
* [`Мокито.ЧисловойПараметр()`](/api/Мокито#числовойпараметр) - параметр может принимать только числовые значения
* [`Мокито.ТипизированныйПараметр()`](/api/Мокито#типизированныйпараметр) - параметр может принимать значения указанного типа
* Предикаты, параметр должен соответствовать заданным условиям.
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, ЮТест.Предикат().НеИмеетСвойства("Авторизация")))
.Вернуть(ОтветНеобходимаАвторизация())
```
##### Варианты обучения (формирования условий)
Существует 2 основных подхода к формированию условий вызова:
1. Явный вызов метода с параметрами
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Данные))
.Вернуть(2)
```
2. Указание имени метода и набора параметров
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(ИсточникДанных, Данные))
.Вернуть(2)
```
Первый вариант имеет ряд недостатков:
1. Работает только для экспортных методов
2. Необходимо передавать все обязательные параметры или использовать для них маску `Мокито.ЛюбойПараметр()`
3. Если не указывать необязательные параметры, то их значения по умолчанию попадут в настройку. Покажу на примере.
```bsl
// Имеется метод
Функция Метод(Параметр1, Параметр2, Параметр3 = 3) Экспорт
Возврат Параметр1 + Параметр2 + Параметр3;
КонецФункции
Мокито.Обучение(...)
.Когда(Метод(1, 2))
.Вернуть(0)
.Прогон();
Метод(1, 2); // Вернет 0
Метод(1, 2, 3); // Вернут 0
Метод(1, 2, 4); // Вернут 7, будет выполнен основной алгоритм
// Второй вариант обучения
Мокито.Обучение(...)
.Когда("Метод", Мокито.МассивПараметров(1, 2))
.Вернуть(0)
.Прогон();
Метод(1, 2); // Вернет 0
Метод(1, 2, 3); // Вернут 0
Метод(1, 2, 4); // Вернут 0
```
#### Действия (Реакции)
После того как определились с условием вызова указанным в методе `Когда` нужно указать реакцию (действие). Возможные реакции:
* [`Вернуть`](/api/МокитоОбучение#вернуть) - вернуть указанное значение
* [`ВыброситьИсключение`](/api/МокитоОбучение#выброситьисключение) - вызвать исключение с переданным текстом
* [`Пропустить`](/api/МокитоОбучение#пропустить) - пропустить выполнение метод (актуально для процедур)
* [`ВыполнитьМетод`](/api/МокитоОбучение#выполнитьметод) - выполнить основной метод конфигурации, обычно комбинируется к другими реакциями, чтобы задать исключение.
Предположим, по умолчанию метод должен выбрасывать исключение, но для одного конкретного случая должен выполниться реальный метод конфигурации. Это проще всего сделать следующим образом:
```bsl
Мокито.Обучение(...)
// По умолчанию метод падает
.Когда("МегаФункция").ВыброситьИсключение("Упал")
// При вызове с параметрами `1, 2, 3` выполняется метод конфигурации
.Когда("МегаФункция", Мокито.МассивПараметров(1, 2, 3)).ВыполнитьМетод()
```
```bsl title="Пример мокирования методов документа"
Документ = СоздатьДокумент();
Мокито.Обучение(Документ)
.Когда("ОбработкаПроверкиЗаполнения").Пропустить() // Отключим проверку заполнения документа
.Когда("КонтрольОстатков").Вернуть(Истина) // Проверка остатков будет успешной
.Когда("ЗафиксироватьОшибки").ВыброситьИсключение("Не должно быть ошибок") // При вызове "лишнего" кода сразу валим тест
.Когда("СформироватьПроводки")
.Пропустить() // Отключаем формирование таблицы движений
.Когда("СформироватьПроводки", Мокито.МассивПараметров("Взаиморасчеты"))
.ВыполнитьМетод() // Но включаем для регистра "Взаиморасчеты"
.Прогон(); // Завершаем настройку
Документ.Записать(РежимЗаписиДокумента.Проведение);
```
##### Цепочки действий
В дополнение к условиям можно формировать цепочки действий, которые будут выполнятся в порядке их "регистрации".
Например, есть метод без параметров, для которого необходимо сэмулировать ситуацию когда он возвращает разные значения.
```bsl
Мокито.Обучение(...)
.Когда("БытьИлиНеБыть")
.Вернуть("Быть") // Первый вызов метода вернет "Быть"
.Вернуть("Или") // Второй вызов
.Вернуть("НеБыть") // Третий
.ВыброситьИсключение("Конец") // Четвертый и все последующие
```
#### Примеры формирования различных вариантов условий
```bsl title="Имеется метод:"
Функция ОтправитьОбъектНаСервер(ИсточникДанных, Объект, HTTPМетод = "POST",
ТипКонтента = "json", Преобразование = Неопределено,
ДопНастройки = Неопределено, Ответ = Неопределено, ОтветВСтруктуру = Ложь,
ТелоОтветаВХранилище = Ложь) Экспорт
```
Предположим необходимо:
* Переопределить все вызовы метода - указываем имя метода без указания параметров
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер").Вернуть(1)
```
* Переопределить вызов, когда первый параметр имеет определенное значение
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(ИсточникДанных))
.Вернуть(2);
// Через явный вызов метода
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Мокито.ЛюбойПараметр())
.Вернуть(2) // Тут используется маска `Мокито.ЛюбойПараметр()`, тк второй параметр является обязательным
```
* Переопределить вызов, когда **второй** параметр имеет определенное значение
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ЛюбойПараметр(), Объект))
.Вернуть(2);
// Через явный вызов метода
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ЛюбойПараметр(), Объект)
.Вернуть(2)
```
* Условие на тип параметра
```bsl
// Первый параметр должен быть ссылкой на справочник, второй числом
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ТипизированныйПараметр(Тип("СправочникСсылка.ИсточникиДанных")), Мокито.ЧисловойПараметр()))
.Вернуть(3);
// Через явный вызов метода
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ТипизированныйПараметр(Тип("СправочникСсылка.ИсточникиДанных")), Мокито.ЧисловойПараметр()))
.Вернуть(3);
```
* На основании порядка вызовов метода
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер")
.Вернуть(1)
.Вернуть(2)
.Вернуть(3)
.ВыброситьИсключение("Конец");
```
### Тестовый прогон
После обучения, настройки реакций на вызовы методов, можно запускать тест нужного метода.
Но перед этим необхдимо перевести `Мокито` в режим **прогона тестов**, для этого используется метод [`Мокито.Прогон()`](/api/Мокито#прогон).
Метод `Прогон` завершает настройку и изменяет режим работы, после этого все вызовы к настроенным методам (добавленным в расширение) будут перехватываться и анализироваться на совпадение условий вызова.
:::info
Также метод `Прогон` очищает накопленную статистику прошлых прогонов. Если вам необходимо ее сохранить передайте `Ложь` в параметрах.
:::
* Для методов, которые были указаны в `Когда()` или `Наблюдать()`, будет собираться статистика вызовов.
* Для методов, у которых есть подходящая "реакция" будет переопределено выполнение и запустится соответствующая реакция (вернуть значение, вызвать исключение и тд)
* Для прочих методов (не настроенных) логика не изменится.
```bsl
// Настройка
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ЛюбойПараметр(), Мокито.ЛюбойПараметр()))
.Вернуть(РезультатПоУмолчанию)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Справочники.ИсточникиДанных.FTP, 2))
.Вернуть(2)
.Прогон(); // Перевод в режим прогона теста
Результат = РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Данные); // Результат будет равен переменной РезультатПоУмолчанию
Результат = РаботаСHTTP.ОтправитьОбъектНаСервер(Справочники.ИсточникиДанных.FTP, 2); // Результат будет равен 2
```
Схема вызовов при вызове замокированного метода.
```mermaid
sequenceDiagram
autonumber
actor Тест
participant Конфигурация
participant Расширение
participant Мокито
participant Контекст
Тест-->Мокито: Мокито.Прогон(). Переходим к тестированию
Тест->>Конфигурация: Вызов метода конфигурации.
Конфигурация->>Расширение: Вызов расширяющего метода.
Расширение->>Расширение: Собирает информацию о вызове
Расширение->>+Мокито: Анализ вызова метода
Мокито->>Контекст: Получение настроек
Контекст-->>Мокито: Настройки мокирования
Мокито->>Мокито: Находит нужную настройку
Мокито-->>Контекст: Сохраняет информацию о вызове метода
Мокито-->>-Расширение: Значение, которое должен вернуть метод
Расширение-->>Тест: Значение, которое должен вернуть метод
```
### Проверка
После прогона теста можно проверить какие методы, с какими параметрами вызывались. Для этих целей необходимо воспользоваться методом [`Мокито.Проверить`](/api/Мокито#проверить)
```bsl
ЛюбойПараметр = Мокито.ЛюбойПараметр();
Мокито.Проверить(РаботаСHTTP) // Устанавливаем проверяемый объект
// Проверка общего количества обращений к методу
.КоличествоВызовов("ОтправитьОбъектНаСервер")
.Заполнено()
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(ЛюбойПараметр, ЛюбойПараметр))
.Равно(3)
// Проверка обращений с фильтром по типам параметров
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ТипизированныйПараметр(ТипИсточникДанных), Мокито.ЧисловойПараметр())) // Условия поиска вызовов
.Больше(1) // Проверки
.Равно(2)
.КоличествоВызовов("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ТипизированныйПараметр(ТипИсточникДанных), Мокито.ЧисловойПараметр()))
.Равно(2)
// Проверка обращений к методу с конкретными параметрами
.КоличествоВызовов("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(1, 2))
.Равно(1)
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(1, 2))
.Равно(1)
```
Принцип формирования проверки:
* Указываем проверяемый объект `Проверить(РаботаСHTTP)`.
* Указываем условия поиска вызовов метода. Логика формирования условия такая же как при обучении.
Например, `КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(ЛюбойПараметр, Мокито.ЧисловойПараметр()))`
Соберет все вызовы метода `РаботаСHTTP.ОтправитьОбъектНаСервер`, к которых вторым параметром идет число, а 3й и последующий параметры имеют значения по умолчанию.
* Проверяем собранные вызовы:
* [`Заполнено`](/api/МокитоПроверки#заполнено) - есть вызовы метода по указанным условиям
* [`Пусто`](/api/МокитоПроверки#пусто) - нет вызовов метода по указанным условиям
* [`Равно`](/api/МокитоПроверки#равно) - количество вызовов попавших под условия равно указанному значению
* [`Больше`](/api/МокитоПроверки#больше) - количество вызовов попавших под условия больше указанного значения
* [`Меньше`](/api/МокитоПроверки#меньше) - количество вызовов попавших под условия меньше указанного значения
#### Статистика вызовов
Также вы можете более детально проанализировать вызовы методов
Для этого можно воспользоваться методом [`Мокито.Проверить(Объект).Вызовы`](/api/МокитоПроверки#вызовы), который возвращает массив с описанием вызовов:
* `Объект` - `Произвольный` - Объект, которому принадлежит метод
* `ИмяМетода` - `Строка` - Имя вызванного метода
* `Параметры` - `Массив` из Произвольный - Набор параметров, с которыми был вызван метод
* `Контекст` - `Строка` - Контекст вызова метода (не реализовано)
```bsl
ВызовыМетода = Мокито.Проверить(Интеграция).Вызовы("ВыполнитьЗапрос");
ЮТест.ОжидаетЧто(ВызовыМетода, "Вызовы метода ВыполнитьЗапрос")
.ИмеетТип("Массив")
.ИмеетДлину(3)
.КаждыйЭлементСодержитСвойствоСоЗначением("Объект", Интеграция)
.КаждыйЭлементСодержитСвойствоСоЗначением("ИмяМетода", "ВыполнитьЗапрос")
.КаждыйЭлементСодержитСвойство("Параметры")
.Свойство("[0].Параметры[0]").Равно("Адрес")
.Свойство("[1].Параметры[0]").Равно(Адрес)
.Свойство("[2].Параметры[1]").Равно(2);
```
## Примеры
### Вызвать исключение при записи объекта
#### Шаг1. Добавляем метод в расширение
Заимствуем в расширение метод `ПередЗаписью`, если обработчик в модуле объекта отсутствует вы все равно можете подписаться таким образом на обработку события. И вставляем код для работы Мокито.
```bsl
&Вместо("ПередЗаписью")
Процедура Мок_ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
ПараметрыМетода = Мокито.МассивПараметров(Отказ, РежимЗаписи, РежимПроведения);
ПрерватьВыполнение = Ложь;
МокитоПерехват.АнализВызова(ЭтотОбъект, "ПередЗаписью", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Отказ, РежимЗаписи, РежимПроведения);
КонецЕсли;
КонецПроцедуры
```
#### Шаг2. Настройка теста
После этого вы можете управлять обработкой события в тесте.
```bsl
Мокито.Обучение(Документы.Оплата)
.Когда("ПередЗаписью").ВыброситьИсключение("Не удалось записать объект")
.Прогон();
Документ = СоздатьДокументОплаты();
ЮТест.ОжидаетЧто(Документ)
.Метод("Записать").Параметр(РежимЗаписиДокумента.Проведение)
.ВыбрасываетИсключение("Не удалось записать объект");
;
```
### Анализ вызовов метода
Предположим у нас есть методы синхронизация данных базы с внешней системой и нам необходимо проверить корректность создания/удаления/изменеиня данных. В общем модуле интеграции созданы методы `СоздатьОбъект`, `УдалитьОбъект`, `ОбновитьОбъект`.
В тесте мы будем формировать различные входные данные и считать вызовы этих методов, проверять чтобы не было лишних вызовов.
#### Шаг1. Добавление методов в расширение
Сначала необходимо выполнить заимствование и настройку методов `СоздатьОбъект`, `УдалитьОбъект`, `ОбновитьОбъект`.
#### Шаг2. Настраиваем мокито
```bsl
Мокито.Обучение(СуперИнтеграция)
.Наблюдать("СоздатьОбъект")
.Наблюдать("УдалитьОбъект")
.Наблюдать("ОбновитьОбъект")
.Прогон();
```
#### Шаг3. Реализация теста
```bsl
ЗаписатьНачальныеДанные(); // Установим начальное состояние базы, создадим нужные данные
// Настроим ответы сервиса, под наши кейсы
Мокито.Обучение(СуперИнтеграция, Ложь) // Важно!. Передаем Ложь, чтобы не затереть ранее установленные настройки
.Когда("ПолучитьДанные")
.Вернуть(ОтветСервисаНовыйЭлемент()) // 1й вызов
.Вернуть(ОтветСервисаИзмененЭлемент()) // 2й вызов
.Вернуть(ОтветСервисаУдаленЭлемент()) // 3й вызов
.Вернуть(ОтветСервисаОшибка()) // 4й вызов
.Вернуть(ОтветСервисаНетДанных()); // 5й вызов
Описание = "Первая итерация, новый элемент";
Мокито.Прогон();
СуперИнтеграция.ВыполнитьСинхронизацию();
Мокито.Проверить(СуперИнтеграция, Описание)
.КоличествоВызовов("СоздатьОбъект").Равно(1)
.КоличествоВызовов("УдалитьОбъект").Пусто()
.КоличествоВызовов("ОбновитьОбъект").Пусто();
Описание = "Вторая итерация, изменен элемент";
Мокито.Прогон(); // Важно!. Повторно вызываем метод, чтобы сбросить ранее накопленную статистику
СуперИнтеграция.ВыполнитьСинхронизацию();
Мокито.Проверить(СуперИнтеграция, Описание)
.КоличествоВызовов("СоздатьОбъект").Пусто()
.КоличествоВызовов("УдалитьОбъект").Пусто()
.КоличествоВызовов("ОбновитьОбъект").Равно(1);
Описание = "Третья итерация, удален элемент";
Мокито.Прогон(); // Важно!. Повторно вызываем метод, чтобы сбросить ранее накопленную статистику
СуперИнтеграция.ВыполнитьСинхронизацию();
Мокито.Проверить(СуперИнтеграция, Описание)
.КоличествоВызовов("СоздатьОбъект").Пусто()
.КоличествоВызовов("УдалитьОбъект").Равно(1)
.КоличествоВызовов("ОбновитьОбъект").Пусто();
// И так далее
```

View File

@ -0,0 +1,228 @@
---
tags: [Начало, Предикаты, Утверждения, Запросы, Мокирование]
sidebar_position: 4
---
# Предикаты
Предикаты это довольно мощный и универсальный инструмент. С помощью предикатов вы формируете набор условий, сродни отбору. Который можно использовать:
1. В утверждениях для [проверки коллекций](assertions/assertions-base.md#проверка-на-соответствие-набору-условий-предикату)
2. В утверждениях для [проверки записей базы](assertions/assertions-db)
3. Для [получения данных базы](auxiliary-modules/queries.md)
4. Для указания условий при [обучении Мокито](mocking/mockito.md#параметры)
Предикаты расширяют и унифицируют функциональность тестового движка.
Механизм предикатов ([ЮТест.Предикат](/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/ЮТПредикаты#получить) - Возвращает набор сформированных утверждений.
Рекомендуется использовать этот метод, если планируется отложенная проверка предикатов. Например, вы хотите сформировать два набору предикатов и проверять их в зависимости от условия.
Метод копирует настроенный набор утверждений в массив и возвращает его, таким образом сохраняется состояние, которое можно передавать дальше.
Возможно создавать предикаты на основании структуры - `ЮТест.Предикат(Структура)`, например:
```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
ПроверкаСтрока = ЮТест.Предикат().ИмеетТип("Строка").Получить();
ПроверкаЧисло = ЮТест.Предикат().ИмеетТип("Число").Получить();
```
### Особенности реализации
Сам модуль предикатов используется только для формирования набора утверждений/условий.
За применение их в разных механизмах, реализацией проверок и формированием условий, отвечают другие модули и возможна ситуация, когда некоторые предикаты еще не реализованы или не поддерживаются каким-либо механизмом. Например, проверка заполненности не поддерживается запросами.

View File

@ -3,11 +3,17 @@ sidebar_position: 9
tags: [Начало]
---
# Форматы отчетов о тестировании
# Отчеты о тестировании
YAxUnit поддерживает генерацию отчетов в форматах `JUnit` и `Allure`.
Важным этапом тестирования является формирование удобного и информативного отчета. YAxUnit предоставляет возможность работать с различными видами отчетов.
Указать необходимый формат вы может в [параметрах запуска](run/configuration), либо в [форме настроек](yaxunit-ui#интерфейс-настройки-конфигурации).
* Отчет в формате JUnit - используется при разработке тестов в EDT, позволяет быстро увидеть результат тестирования прямов в IDE, позволяет переходить по стеку ошибок и сравнивать фактическое и ожидаемое значение.
* Отчет в формате Allure - чаще всего применяется в CI, имеет богатую функциональность: фильтрация, различные группировки, история и статистика.
* Отчет в режиме 1С:Предприятия - удобен если вы не используете (или не можете использовать) по какой-либо причине EDT.
![reports](images/reports.png)
Указать необходимый формат вы может в [параметрах запуска](../getting-started/run/configuration), либо в [форме настроек](../yaxunit-ui#интерфейс-настройки-конфигурации).
По умолчанию используется формат `JUnit`.
## Формат `JUnit`
@ -24,4 +30,4 @@ YAxUnit поддерживает генерацию отчетов в форма
Группировка по функциональности
![Группировка по функциональности](images/allure-report-behaviors.png)
![Группировка по функциональности](images/allure-report-behaviors.png)

View File

@ -0,0 +1,306 @@
---
tags: [Начало, Тестовые данные]
sidebar_position: 1
---
# Программное создание тестовых данных
Доступ к методам генерации тестовых реализован через [`ЮТест.Данные()`](/api/ЮТест#данные), этот метод возвращает общий модуль [`ЮТТестовыеДанные`](/api/ЮТТестовыеДанные).
:::tip
Не рекомендуется обращаться к модулю `ЮТТестовыеДанные` напрямую, используйте `ЮТест.Данные()`
:::
## Возможности
Инструменты работы с тестовыми данными можно разделить на следующие группы:
* Генераторы случайных значений
* Методы создания данных в информационной базе
* Методы загрузки из макетов
* Работа с файлами
### Генераторы случайных данных
Как уже говорилось в [статье](test-data.md) в YAxUnit сделан большой упор на генерацию случайных тестовых данных, которая позволяет улучшить покрытие и сократить количество "лишнего" кода. Поставив в самом начале на такой подход мы еще ни разу в нем не разочаровались.
Создание случайных данных базируется на генерации примитивных значения:
* Числа
* [`ЮТест.Данные().СлучайноеЧисло`](/api/ЮТТестовыеДанные#случайноечисло) - Формирует случайное число в указанном диапазоне
* [`ЮТест.Данные().СлучайноеПоложительноеЧисло`](/api/ЮТТестовыеДанные#случайноеположительноечисло) - Формирует случайное положительное число ограниченное максимумом
* [`ЮТест.Данные().СлучайноеОтрицательноеЧисло`](/api/ЮТТестовыеДанные#случайноеотрицательноечисло) - Формирует случайное отрицательное число ограниченное минимумом
* Строки
* [`ЮТест.Данные().СлучайнаяСтрока`](/api/ЮТТестовыеДанные#случайнаястрока) - Формирует случайную строку указанной длины
* [`ЮТест.Данные().СлучайныйИдентификатор`](/api/ЮТТестовыеДанные#случайныйидентификатор) - Формирует случайный валидный идентификатор (Первым символом должна быть буква или символ подчеркивания, каждый из последующих символов может быть буквой, цифрой или символом подчеркивания)
* [`ЮТест.Данные().СлучайныйIPАдрес`](/api/ЮТТестовыеДанные#случайныйipадрес) - Формирует случайный IP адрес
* [`ЮТест.Данные().СлучайныйНомерТелефона`](/api/ЮТТестовыеДанные#случайныйномертелефона) - Формирует случайный номер телефона
* [`ЮТест.Данные().УникальнаяСтрока`](/api/ЮТТестовыеДанные#уникальнаястрока) - Создает строку на базе нового уникального идентификатора
* Даты
* [`ЮТест.Данные().СлучайнаяДата`](/api/ЮТТестовыеДанные#случайнаядата) - Формирует случайную дату в указанном диапазоне
* [`ЮТест.Данные().СлучайноеВремя`](/api/ЮТТестовыеДанные#случайноевремя) - Формирует случайное время
* [`ЮТест.Данные().СлучайнаяДатаВБудущем`](/api/ЮТТестовыеДанные#случайнаядатавбудущем) - Формирует случайную дату в будущем (больше текущей) с возможностью ограничить максимальное значение.
Пример: `СлучайнаяДатаВБудущем(2, "часа")` - будет сформирована дата в интервале (ТекущаяДата, ТекущаяДата + 2 часа]
* [`ЮТест.Данные().СлучайнаяДатаВПрошлом`](/api/ЮТТестовыеДанные#случайнаядатавпрошлом) - Формирует случайную дату в прошлом (меньше текущей) с возможностью ограничить минимальное значение.
Пример: `СлучайнаяДатаВПрошлом(2, "часа")` - будет сформирована дата в интервале [ТекущаяДата - 2 часа, ТекущаяДата)
* [`ЮТест.Данные().СлучайнаяДатаПосле`](/api/ЮТТестовыеДанные#случайнаядатапосле) - Формирует случайную дату, которая больше указанной с возможностью ограничить максимальное значение.
Пример: `СлучайнаяДатаПосле(Дата, 2, "часа")` - будет сформирована дата в интервале (Дата, Дата + 2 часа]
* [`ЮТест.Данные().СлучайнаяДатаДо`](/api/ЮТТестовыеДанные#случайнаядатадо) - Формирует случайную дату, которая меньше указанной с возможностью ограничить минимальное значение.
Пример: `СлучайнаяДатаДо(Дата, 2, "часа")` - будет сформирована дата в интервале [Дата - 2 часа, Дата)
* Булево
* [`ЮТест.Данные().СлучайноеБулево`](/api/ЮТТестовыеДанные#случайноебулево) - Возвращает случайное булево
* Получение случайного из предопределенного списка
* [`ЮТест.Данные().СлучайноеЗначениеИзСписка`](/api/ЮТТестовыеДанные#случайноезначениеизсписка) - Возвращает случайное значение из указанного списка
```bsl
ВозможныеЗначения = ЮТКоллекции.ЗначениеВМассиве(-1, 0, 1);
Возврат ЮТест.Данные().СлучайноеЗначениеИзСписка(ВозможныеЗначения);
```
* [`ЮТест.Данные().СлучайноеЗначениеПеречисления`](/api/ЮТТестовыеДанные#случайноезначениеперечисления) - Возвращает случайное значение перечисления
Примеры:
```bsl
Цена = ЮТест.Данные().СлучайноеЗначениеПеречисления(Перечисления.ВидыЦен); // Можно получить значение через указание менеджера
Цена = ЮТест.Данные().СлучайноеЗначениеПеречисления("Перечисление.ВидыЦен"); // Или имя объекта метаданных (подходит для работы с клиента)
```
* [`ЮТест.Данные().СлучайноеПредопределенноеЗначение`](/api/ЮТТестовыеДанные#случайноепредопределенноезначение) - Возвращает случайное предопределенное значение
Примеры:
```bsl
Цена = ЮТест.Данные().СлучайноеПредопределенноеЗначение(Справочники.ВидыЦен); // Можно получить значение через указание менеджера
Цена = ЮТест.Данные().СлучайноеПредопределенноеЗначение("Справочник.ВидыЦен"); // Или имя объекта метаданных (подходит для работы с клиента)
```
* [`ЮТест.Данные().Подражатель`](/api/ЮТТестовыеДанные#подражатель) - генератор правдоподобных данных. Генерирует случайные данные на базе словарей, является реализацией [faker](https://github.com/faker-js/faker) для 1С.
* [`ЮТест.Данные().Подражатель().Люди`](/api/ЮТПодражатель#люди)
* [`ФИО`](/api/ЮТПодражатель_Люди#фио) - Формирует случайное ФИО
* [`Фамилия`](/api/ЮТПодражатель_Люди#фамилия) - Формирует случайную фамилию
* [`Имя`](/api/ЮТПодражатель_Люди#имя) - Формирует случайное имя
* [`Отчество`](/api/ЮТПодражатель_Люди#отчество) - Формирует случайное отчество
* [`ИНН`](/api/ЮТПодражатель_Люди#инн) - Формирует случайный ИНН
* [`СНИЛС`](/api/ЮТПодражатель_Люди#снилс) - Формирует случайный СНИЛС
* [`ЮТест.Данные().Подражатель().Компании`](/api/ЮТПодражатель#компании)
* [`Наименование`](/api/ЮТПодражатель_Компании#наименование) - Формирует случайное наименование компании
* [`ИНН`](/api/ЮТПодражатель_Компании#инн) - Формирует случайный ИНН компании
* [`КПП`](/api/ЮТПодражатель_Компании#кпп) - Формирует случайный КПП компании
* [`ЮТест.Данные().Подражатель().Банки`](/api/ЮТПодражатель#банки)
* [`НомерСчета`](/api/ЮТПодражатель_Банки#номерсчета) - Формирует случайный номер банковского счета
* [`БИК`](/api/ЮТПодражатель_Банки#бик) - Формирует случайный валидный БИК банка
### Методы создания данных в информационной базе
* Создание пустышек - примитивных объектов с минимальным заполнения. Полезно использовать когда вам нужна ссылка определенного типа, но не важно ее заполнение
* [`ЮТест.Данные().СоздатьЭлемент`](/api/ЮТТестовыеДанные#создатьэлемент) - Создает новый элемент справочника или плана видов характеристик
```bsl
Товар = ЮТест.Данные().СоздатьЭлемент(Справочники.Товары); // На основании менеджера
Товар = ЮТест.Данные().СоздатьЭлемент("Справочники.Товары"); // На основании имени менеджера
ПараметрыЗаписи = ЮТФабрика.ПараметрыЗаписи();
ПараметрыЗаписи.ОбменДаннымиЗагрузка = Истина;
Товар = ЮТест.Данные().СоздатьЭлемент("Справочники.Товары", , , ПараметрыЗаписи); // Запись в режиме ОбменДанными.Загрузка = Истина
```
* [`ЮТест.Данные().СоздатьДокумент`](/api/ЮТТестовыеДанные#создатьдокумент) - Создает новый документ
```bsl
Товар = ЮТест.Данные().СоздатьДокумент(Документ.ПриходТовара); // На основании менеджера
Товар = ЮТест.Данные().СоздатьДокумент("Документ.ПриходТовара"); // На основании имени менеджера
```
* [`ЮТест.Данные().СоздатьГруппу`](/api/ЮТТестовыеДанные#создатьгруппу) - Создает новую группу справочника или плана видов характеристик
```bsl
Товар = ЮТест.Данные().СоздатьГруппу(Документ.ПриходТовара); // На основании менеджера
Товар = ЮТест.Данные().СоздатьГруппу("Документ.ПриходТовара"); // На основании имени менеджера
```
* [`ЮТест.Данные().Фикция`](/api/ЮТТестовыеДанные#фикция) - Создает новый объект на основании его типа
```bsl
Товар = ЮТест.Данные().Фикция(Тип("ДокументСсылка.ПриходТовара"));
Товар = ЮТест.Данные().Фикция(Новый ОписаниеТипов("ДокументСсылка.ПриходТовара"));
Товар = ЮТест.Данные().Фикция(Новый ОписаниеТипов("ДокументСсылка.ПриходТовара, ДокументСсылка.РасходТовара")); // Будет выбран случайный тип
```
* [`ЮТест.Данные().КонструкторОбъекта`](/api/ЮТТестовыеДанные#конструкторобъекта), с помощью которого вы можете создать объект информационной базы с необходимыми реквизитами.
Конструктор позволяет:
* В более компактной форме заполнять объект
* Имеет методы записи и проведения
* При возникновении ошибки оборачивает их в более понятные человеку описания
* Киллер фича - `Фикция` и `ФикцияОбязательныхПолей`. Устанавливает случайно сгенерированные значения реквизитов. Таким образом делается акцент на важных для теста реквизитах и экономит "строки" на генерацию ненужных значений
* Умеет создавать данные с клиента и [удалять их по окончании теста](test-data-deletion.md)
```bsl
Конструктор = ЮТест.Данные().КонструкторОбъекта("Документы.ПриходТовара")
.Фикция("Поставщик")
.Фикция("Склад")
.ФикцияОбязательныхПолей()
.ТабличнаяЧасть("Товары");
ОбщиеДанные = Новый Структура("ВидЦены, Поставщик", Закупочная, Конструктор.ДанныеОбъекта().Поставщик);
Для Инд = 1 По 3 Цикл
Конструктор.ДобавитьСтроку()
.Фикция("Товар")
.УстановитьРеквизиты(ОбщиеДанные)
.Установить("Цена", ЮТест.Данные().СлучайноеПоложительноеЧисло(9999, 2))
.Установить("Количество", ЮТест.Данные().СлучайноеПоложительноеЧисло(20))
.Установить("Сумма", Конструктор.ДанныеСтроки().Цена * Конструктор.ДанныеСтроки().Количество)
КонецЦикла;
Возврат Конструктор.Провести();
```
### Загрузка данных из макетов
Работа с макетами описана в отдельной [статье](load-from-templates.md)
### Работа с файлами
* [`ЮТест.Данные().НовыйФайл`](/api/ЮТТестовыеДанные#новыйфайл) - Создает новый файл, который будет удален после теста
* [`ЮТест.Данные().НовоеИмяВременногоФайла`](/api/ЮТТестовыеДанные#новоеимявременногофайла) - Возвращает имя нового файла, по окончании выполнения теста этот файл будет удален
### Создание XDTO
При тестировании обменов или интеграций использующих SOAP вам может пригодится [`ЮТест.Данные().КонструкторОбъектаXDTO`](/api/ЮТТестовыеДанные#конструкторобъектаxdto), с помощью которого вы можете с легкостью создать XDTO объект.
```bsl
Тип = СериализаторXDTO.XMLТип(Тип("ДокументОбъект.Заказ"));
Дата = ЮТест.Данные().СлучайнаяДата();
Номер = ЮТест.Данные().СлучайнаяСтрока(9);
Сумма = ЮТест.Данные().СлучайноеЧисло();
Реквизиты = Новый Структура("Number, Сумма", Номер, Сумма);
Объект = ЮТест.Данные().КонструкторОбъектаXDTO(Тип.ИмяТипа, Тип.URIПространстваИмен)
.Установить("Date", Дата)
.ФикцияОбязательныхПолей()
.УстановитьРеквизиты(Реквизиты)
.Фикция("Покупатель")
.ДобавитьНовый("Товары")
.Фикция("Цена")
.Фикция("Количество")
.ДанныеОбъекта();
```
Он был создан на основании уже проверенного и зарекомендовавшего себя `ЮТест.Данные().КонструкторОбъекта` и обладает схожим API.
* `Установить(ИмяРеквизита, Значение)` - Устанавливает значение указанного реквизита объекта.
* `УстановитьРеквизиты(ЗначенияРеквизитов)` - Устанавливает значения реквизитов объекта.
* `Фикция(ИмяРеквизита)` - на основании типа создаваемого объекта определяет тип реквизита и устанавливает для него случайное значение.
* Для примитивных значение генерируется случайное значение
* Для объектных типов создается новый объект
* Для коллекций - генерируется случайно количество случайных элементов (на основании типа)
* `ФикцияОбязательныхПолей()` - на основании типа создаваемого объекта определяет список обязательных полей и для каждого из них вызывается метод `Фикция`.
* `ФикцияНезаполненных()` - для всех незаполненных полей создаваемого объекта вызывает метод `Фикция`.
* `ДобавитьНовый(ИмяРеквизита)` - Добавляет новый объект в указанную реквизит-коллекцию. Все последующие вызовы установки реквизитов будут относится к этому добавленному объекту.
* `ПерейтиКВладельцу()` - используется после метода `ДобавитьНовый` для переключения контекста заполнения на родительский объект. Например вам необходимо добавить две строки в табличную часть.
```bsl
Объект = ЮТест.Данные().КонструкторОбъектаXDTO(Тип.ИмяТипа, Тип.URIПространстваИмен)
.ДобавитьНовый("Товары")
.Фикция("Цена")
.Фикция("Количество")
.ПерейтиКВладельцу()
.ДобавитьНовый("Товары")
.Фикция("Цена")
.Фикция("Количество")
.ДанныеОбъекта();
```
Здесь перед добавлением второй строки нужно переключится (`ПерейтиКВладельцу`) на основной объект и после этого можно добавить строку.
* `ДанныеОбъекта()` - возвращает созданный объект XDTO.
## Примеры
### Генерация фейкового документа
```bsl
#Если Сервер Тогда
Конструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара);
#Иначе
Конструктор = ЮТест.Данные().КонструкторОбъекта("Документы.ПриходТовара");
#КонецЕсли
Конструктор
.Фикция("Поставщик")
.Фикция("Склад")
.Фикция("Валюта")
.Установить("Организация", ЮТест.Данные().КонструкторОбъекта("Справочники.Организации").Установить("КакойТоТамУчет", Истина))
.ТабличнаяЧасть("Товары");
Для Инд1 = 1 По ЮТест.Данные().СлучайноеЧисло(1, 5) Цикл
Конструктор.ДобавитьСтроку()
.Фикция("Товар")
.Установить("Цена", ЮТест.Данные().СлучайноеПоложительноеЧисло(9999, 2))
.Установить("Количество", ЮТест.Данные().СлучайноеПоложительноеЧисло(20))
.Установить("Сумма", Конструктор.ДанныеСтроки().Цена * Конструктор.ДанныеСтроки().Количество)
КонецЦикла;
Ссылка = Конструктор.Провести();
```
### Генерация произвольных фейковых данных
```bsl
Функция ОписаниеСервера(Знач Идентификатор = Неопределено) Экспорт
Описание = БазовоеОписаниеОбъекта(Идентификатор, "СРВ");
Описание.Вставить("Адрес", ЮТТестовыеДанные.СлучайныйIPАдрес());
Описание.Вставить("ЧастотаЯдра", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(4, 3));
Описание.Вставить("КоличествоЯдер", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(10));
Описание.Вставить("КоличествоПотоков", Описание.КоличествоЯдер * 2);
Описание.Вставить("ПроизводительностьПроцессора", Описание.ЧастотаЯдра * Описание.КоличествоЯдер);
Описание.Вставить("ОбъемПамяти", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(1024, 3));
Описание.Вставить("Диски", Новый Массив());
Описание.Вставить("Кластер");
Для Инд = 1 По ЮТТестовыеДанные.СлучайноеЧисло(1, 3) Цикл
Описание.Диски.Добавить(ОписаниеДиска());
КонецЦикла;
Возврат Описание;
КонецФункции
Функция ОписаниеЗаявкиНаСозданиеВМ() Экспорт
Описание = Новый Структура();
Описание.Вставить("Наименование", "req-" + ЮТТестовыеДанные.СлучайнаяСтрока());
Описание.Вставить("ДатаДобавления", глПолучитьМосковскоеВремя());
Описание.Вставить("ДатаОкончанияАренды", глДобавитьКДате(глПолучитьМосковскоеВремя(), "ДЕНЬ", 2));
Описание.Вставить("Адрес", ЮТТестовыеДанные.СлучайныйIPАдрес());
Описание.Вставить("Проект", Проект());
Описание.Вставить("Ментейнер", ПараметрыСеанса.Сотрудник);
Описание.Вставить("КоличествоCPU", ЮТТестовыеДанные.СлучайноеЧисло(1, 4));
Описание.Вставить("КоличествоПамяти", ЮТТестовыеДанные.СлучайноеЧисло(1, 16));
Описание.Вставить("КоличествоДисков", ЮТТестовыеДанные.СлучайноеЧисло(1, 16));
Описание.Вставить("ОперационнаяСистема", ЮТТестовыеДанные.СоздатьЭлемент(Справочники.ОперационныеСистемы));
Описание.Вставить("СредаЭксплуатации", "");
Описание.Вставить("Назначение", ЮТТестовыеДанные.СоздатьЭлемент(Справочники.НазначенияВиртуальныхМашин));
Описание.Вставить("ТипСреды", Справочники.ТипыИнформационныхСред.BETA);
Описание.Вставить("КодСервиса", ЮТТестовыеДанные.СлучайнаяСтрока(1));
Возврат Описание;
КонецФункции
Функция НовыйОбразDocker(Версия, ПоУмолчанию = Истина) Экспорт
Данные = Новый Структура("Адрес, ВерсияПлатформы, ТипПлатформы, ИспользоватьПоУмолчанию");
Данные.Адрес = "gitlab.ru/orais/ci_cd/1cws-apache24:" + Версия;
Данные.ВерсияПлатформы = Версия;
Данные.ТипПлатформы = Перечисления.ТипыСоединенияИсточникаДанных.Соединение1СПредприятие83Сервер;
Данные.ИспользоватьПоУмолчанию = ПоУмолчанию;
Возврат ЮТТестовыеДанные.СоздатьЭлемент(Справочники.ОбразыDocker, Версия, Данные);
КонецФункции
```

View File

@ -0,0 +1,6 @@
# Дополнительные методы работы с данными
* [`ЮТест.Данные().Удалить`](/api/ЮТТестовыеДанные#удалить) - Выполняет удаление переданных записей из базы
* [`ЮТест.Данные().УстановитьЗначениеРеквизита`](/api/ЮТТестовыеДанные#установитьзначениереквизита) - Устанавливает значение реквизита ссылки
* [`ЮТест.Данные().УстановитьЗначенияРеквизитов`](/api/ЮТТестовыеДанные#установитьзначенияреквизитов) - Устанавливает значения реквизитов ссылки
* [`ЮТЗапросы`](/api/ЮТЗапросы) - Расширяет возможности тестирования, позволяет в упрощенной форме получать данны из информационной базы как с сервера, так и с клиента.

View File

@ -1,182 +1,13 @@
---
tags: [Начало, Тестовые данные]
sidebar_position: 3
---
# Тестовые данные
Почти в каждом тесте разработчику необходимы данные, которые он будет использовать при тестировании: элементы справочников, документы, остатки и тд.
# Загрузка данных из макетов
Есть разные подходы к работе с тестовыми данными:
## Описание
1. Ручное наполнение тестовой базы (мы предварительно создаем все что необходимо для тестов в базе, а потом используем её для прогона тестов)
2. Использование файлов с данными (макеты - сгенерированные в нужном формате файлы с описанием тестовых данных, загружаемые при выполнении тестов)
3. Программное создание нужных данных внутри теста
У каждого из вариантов есть свои плюсы и минусы, и при грамотной компоновке можно достичь наилучшего результата.
Например:
* Ручное наполнение базы не учитывает последующие доработки системы, нужна миграция на новые алгоритмы и структуру, и, если таких данных будет много, то миграция будет занимать большое время. А также в этом случае нам необходимо завязываться на конкретные записи в системе, и при их изменении тестом мы можем сломать другие тесты. Поэтому, предварительное наполнение базы должно быть минимальным.
* В случае использования макетов данных возникают некоторые трудности с их доработкой и вариативностью, например, в новом тесте нужны данные из макета, но с некоторыми изменениями. В этом случае нам потребуется создавать новый макет или проверять, не сломают ли наши изменения другие тесты. Также при таком подходе сложно контролировать и искать какие макеты используются и кем.
* Программное создание тестовых данных увеличивает время и сложность теста.
Если грамотно распределить тестовые данные на способы создания, мы можем добиться оптимального результата.
Например, можно статичные данные создать вручную, сложные кейсы данных засунуть в макеты, а для программного создания реализовать методы-конструкторы, которые будем переиспользовать. Таким образом, сможем уйти от минусов данного подхода или минимизировать их вред.
В текущей версии тестовый движок предоставляет api для программного создания тестовых данных. За это отвечает общий модуль `ЮТТестовыеДанные`, к которому можно обратиться через метод `ЮТест.Данные()`.
:::tip
Не рекомендуется обращаться к модулю `ЮТТестовыеДанные` напрямую, используйте `ЮТест.Данные()`
:::
Он позволяет:
* Создавать данные информационной базы
* Генерировать случайные значения
* Автоматически удалять созданные данные (для этого необходимо включить настройку теста `УдалениеТестовыхДанных()`)
* Работать с файлами
* Использовать таблицы markdown в качестве макетов данных
* Генерировать таблицы значений из табличных документов и таблиц markdown
## Примеры
### Генерация фейкового документа
```bsl
#Если Сервер Тогда
Конструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПриходТовара);
#Иначе
Конструктор = ЮТест.Данные().КонструкторОбъекта("Документы.ПриходТовара");
#КонецЕсли
Конструктор
.Фикция("Поставщик")
.Фикция("Склад")
.Фикция("Валюта")
.Установить("Организация", ЮТест.Данные().КонструкторОбъекта("Справочники.Организации").Установить("КакойТоТамУчет", Истина))
.ТабличнаяЧасть("Товары");
Для Инд1 = 1 По ЮТест.Данные().СлучайноеЧисло(1, 5) Цикл
Конструктор.ДобавитьСтроку()
.Фикция("Товар")
.Установить("Цена", ЮТест.Данные().СлучайноеПоложительноеЧисло(9999, 2))
.Установить("Количество", ЮТест.Данные().СлучайноеПоложительноеЧисло(20))
.Установить("Сумма", Конструктор.ДанныеСтроки().Цена * Конструктор.ДанныеСтроки().Количество)
КонецЦикла;
Ссылка = Конструктор.Провести();
```
### Генерация произвольных фейковых данных
```bsl
Функция ОписаниеСервера(Знач Идентификатор = Неопределено) Экспорт
Описание = БазовоеОписаниеОбъекта(Идентификатор, "СРВ");
Описание.Вставить("Адрес", ЮТТестовыеДанные.СлучайныйIPАдрес());
Описание.Вставить("ЧастотаЯдра", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(4, 3));
Описание.Вставить("КоличествоЯдер", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(10));
Описание.Вставить("КоличествоПотоков", Описание.КоличествоЯдер * 2);
Описание.Вставить("ПроизводительностьПроцессора", Описание.ЧастотаЯдра * Описание.КоличествоЯдер);
Описание.Вставить("ОбъемПамяти", ЮТТестовыеДанные.СлучайноеПоложительноеЧисло(1024, 3));
Описание.Вставить("Диски", Новый Массив());
Описание.Вставить("Кластер");
Для Инд = 1 По ЮТТестовыеДанные.СлучайноеЧисло(1, 3) Цикл
Описание.Диски.Добавить(ОписаниеДиска());
КонецЦикла;
Возврат Описание;
КонецФункции
Функция ОписаниеЗаявкиНаСозданиеВМ() Экспорт
Описание = Новый Структура();
Описание.Вставить("Наименование", "req-" + ЮТТестовыеДанные.СлучайнаяСтрока());
Описание.Вставить("ДатаДобавления", глПолучитьМосковскоеВремя());
Описание.Вставить("ДатаОкончанияАренды", глДобавитьКДате(глПолучитьМосковскоеВремя(), "ДЕНЬ", 2));
Описание.Вставить("Адрес", ЮТТестовыеДанные.СлучайныйIPАдрес());
Описание.Вставить("Проект", Проект());
Описание.Вставить("Мейнтейнер", ПараметрыСеанса.Сотрудник);
Описание.Вставить("КоличествоCPU", ЮТТестовыеДанные.СлучайноеЧисло(1, 4));
Описание.Вставить("КоличествоПамяти", ЮТТестовыеДанные.СлучайноеЧисло(1, 16));
Описание.Вставить("КоличествоДисков", ЮТТестовыеДанные.СлучайноеЧисло(1, 16));
Описание.Вставить("ОперационнаяСистема", ЮТТестовыеДанные.СоздатьЭлемент(Справочники.ОперационныеСистемы));
Описание.Вставить("СредаЭксплуатации", "");
Описание.Вставить("Назначение", ЮТТестовыеДанные.СоздатьЭлемент(Справочники.НазначенияВиртуальныхМашин));
Описание.Вставить("ТипСреды", Справочники.ТипыИнформационныхСред.BETA);
Описание.Вставить("КодСервиса", ЮТТестовыеДанные.СлучайнаяСтрока(1));
Возврат Описание;
КонецФункции
Функция НовыйОбразDocker(Версия, ПоУмолчанию = Истина) Экспорт
Данные = Новый Структура("Адрес, ВерсияПлатформы, ТипПлатформы, ИспользоватьПоУмолчанию");
Данные.Адрес = "gitlab.ru/orais/ci_cd/1cws-apache24:" + Версия;
Данные.ВерсияПлатформы = Версия;
Данные.ТипПлатформы = Перечисления.ТипыСоединенияИсточникаДанных.Соединение1СПредприятие83Сервер;
Данные.ИспользоватьПоУмолчанию = ПоУмолчанию;
Возврат ЮТТестовыеДанные.СоздатьЭлемент(Справочники.ОбразыDocker, Версия, Данные);
КонецФункции
```
### Чтение из таблицы Markdown
```bsl
Макет =
"| Имя | ИмяКоллекции | Конструктор | Группы | Ссылочный | Реквизиты | Измерения | Ресурсы | РеквизитыАдресации | ТабличныеЧасти |
||------------------------|-------------------------|----------------------|--------|-----------|-----------|-----------|---------|--------------------|----------------|
|| Справочник | Справочники | СоздатьЭлемент | + | + | + | | | | + |
|| Документ | Документы | СоздатьДокумент | | + | + | | | | + |
|| ПланВидовХарактеристик | ПланыВидовХарактеристик | СоздатьЭлемент | + | + | + | | | | + |
|| ПланСчетов | ПланыСчетов | СоздатьСчет | | + | + | | | | + |
|| ПланВидовРасчета | ПланыВидовРасчета | СоздатьВидРасчета | | + | + | | | | + |
|| ПланОбмена | ПланыОбмена | СоздатьУзел | | + | + | | | | + |
|| РегистрСведений | РегистрыСведений | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрНакопления | РегистрыНакопления | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрБухгалтерии | РегистрыБухгалтерии | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрРасчета | РегистрыРасчета | СоздатьНаборЗаписей | | | + | + | + | | |
|| БизнесПроцесс | БизнесПроцессы | СоздатьБизнесПроцесс | | + | + | | | | + |
|| Задача | Задачи | СоздатьЗадачу | | + | + | | | + | + |
|";
КоллекцияОписаний = ЮТТестовыеДанные.ТаблицаMarkDown(Макет);
ТипыМетаданных = Новый Структура();
Для Каждого Запись Из КоллекцияОписаний Цикл
Описание = Новый Структура();
Описание.Вставить("Имя", Запись.Имя);
Описание.Вставить("ИмяКоллекции", Запись.ИмяКоллекции);
Описание.Вставить("Конструктор", Запись.Конструктор);
Описание.Вставить("Группы", Запись.Группы = "+");
Описание.Вставить("Ссылочный", Запись.Ссылочный = "+");
Описание.Вставить("Реквизиты", Запись.Реквизиты = "+");
Описание.Вставить("Измерения", Запись.Измерения = "+");
Описание.Вставить("Ресурсы", Запись.Ресурсы = "+");
Описание.Вставить("РеквизитыАдресации", Запись.РеквизитыАдресации = "+");
Описание.Вставить("ТабличныеЧасти", Запись.ТабличныеЧасти = "+");
ТипыМетаданных.Вставить(Описание.Имя, Описание);
ТипыМетаданных.Вставить(Описание.ИмяКоллекции, Описание);
КонецЦикла;
Возврат ТипыМетаданных;
```
### Загрузка данных из макетов
Для загрузки данных из макетов подойдет метод `ЮТест.Данные().ЗагрузитьИзМакета`
Для загрузки данных из макетов подойдет метод [`ЮТест.Данные().ЗагрузитьИзМакета`](/api/ЮТТестовыеДанные#загрузитьизмакета)
Он позволяет:
@ -197,26 +28,34 @@ tags: [Начало, Тестовые данные]
Особенности:
* Создание данных
* Данные в базе всегда создаются, не выполняется поиск уже существующих ссылок. При необходимости вы можете найти данные сами и передать в параметрах `ЗаменяемыеЗначения` или `КэшЗначений`
:::tip
При преобразовании табличного документа в таблицу значений существующие объекты метаданных не изменяются, только создаются новые. Нужно проявлять внимательность в случаях, если в базе уже имеются данные с тем же кодом/наименованием (и по ним используется контроль уникальности), что и в табличном документе.
:::
* Можно указывать значения вложенных реквизитов в других колонках, например для колонки `Товар` можно добавить колонки с поставщиком и видом товара `Товар.Поставщик` и `Товар.Вид`, в которых указать нужные значения
* Переиспользование созданных данных, используется `КэшЗначений`, в который помещаются все созданные данные, если кэш содержит данные подходящего типа с указанным идентификатором, то используется ранее созданное значение из кэша. Например, в примере выше, для первой строки будет создан товар с идентификатором `Товар 1`. В строках 2 и 3 будет использовано значение из кэша - `Товар 1`, в 4й и 5й строке - созданы новые товары.
* Данные в базе всегда создаются, не выполняется поиск уже существующих ссылок. При необходимости вы можете найти данные сами и передать в параметрах `ЗаменяемыеЗначения`
:::tip
При преобразовании табличного документа в таблицу значений существующие объекты метаданных не изменяются, только создаются новые. Нужно проявлять внимательность в случаях, если в базе уже имеются данные с тем же кодом/наименованием (и по ним используется контроль уникальности), что и в табличном документе.
:::
* Можно указывать значения вложенных реквизитов, например при создании товаров (колонка `Товар`) необходимо указать конкретные значения поставщика и вида товара. Для этого необходимо добавить колонки `Товар.Поставщик` и `Товар.Вид`, в которых указать нужные значения.
* Переиспользование созданных данных, используется `КэшЗначений`, в который помещаются все созданные данные, если кэш содержит данные подходящего типа с указанным идентификатором, то используется ранее созданное значение из кэша. Например, в примере выше, для первой строки будет создан товар с идентификатором `Товар 1`. В строках 2 и 3 будет использовано значение из кэша - `Товар 1`, в 4й и 5й строке - созданы новые товары.
:::tip
Значение в колонке "Товар" является идентификатором ссылки. Для справочника значение этой колонки по умолчанию записывается в реквизит "Наименование" или "Код", в зависимости от того, какой из этих реквизитов является основным представлением. Для документа это значение никуда не записывается, оно лишь является идентификатором. Таким образом, если одному значению в колонке "Товар" соответствуют различные значения в других колонках, то в элемент справочника запишутся реквизиты из первой строки, а для других строк ссылка на этот элемент будет проставлена по соответствующему идентификатору
:::
:::tip
Кэш значений может передаваться между загрузками разных макетов, например есть макет с табличной частью документа и второй макет с набором ожидаемых движений. Чтобы созданные товары из табличной части соответствовали товарам движений необходимо передать `КэшЗначений` между загрузками.
#### Загрузка всего макета
```bsl
КэшЗначений = Неопределено;
ТабличнаяЧасть = ЮТест.Данные().ЗагрузитьИзМакета("ОбщийМакет.ДанныеДокумента.ТабличнаяЧасть", ОписанияТиповТЧ, КэшЗначений);
ОжидаемыеДвижения = ЮТест.Данные().ЗагрузитьИзМакета("ОбщийМакет.ДанныеДокумента.ДвиженияРегистра1", ОписанияТиповРегистра1, КэшЗначений);
```
:::
:::tip
Значение в колонке "Товар" является идентификатором ссылки. Для справочника значение этой колонки по умолчанию записывается в реквизит "Наименование" или "Код", в зависимости от того, какой из этих реквизитов является основным представлением. Для документа это значение никуда не записывается, оно лишь является идентификатором. Таким образом, если одному значению в колонке "Товар" соответствуют различные значения в других колонках, то в элемент справочника запишутся реквизиты из первой строки, а для других строк ссылка на этот элемент будет проставлена по соответствующему идентификатору
:::
## Примеры
### Загрузка всего макета
В качестве источника данных можно использовать весь табличный документ. В этом случае будет получена таблица значений, начиная с 1-ой строки и 1-ой колонки табличного документа до первой пустой строки.
:::info
Этот код работает и на клиенте и на сервер. Для сервера результатом будет таблица значений, для клиента - массив структур
:::
_Исходные данные_
| Товар | Товар.Поставщик | Товар.Вид | Цена | Количество | Сумма |
@ -227,6 +66,10 @@ _Исходные данные_
| Товар 2 | Поставщик 1 | Товар | 2 000 | 1 | 2000 |
| Услуга | | Услуга | 300,5 | 1 | 300.5 |
:::info
Этот код работает и на клиенте и на сервер. Для сервера результатом будет таблица значений, для клиента - массив структур
:::
```bsl
ОписанияТипов = Новый Соответствие;
ОписанияТипов.Вставить("Товар", Новый ОписаниеТипов("СправочникСсылка.Товары"));
@ -236,7 +79,7 @@ _Исходные данные_
ТаблицаТоваров = ЮТест.Данные().ЗагрузитьИзМакета("ОбщийМакет.ЮТ_МакетТестовыхДанных", ОписанияТипов);
Ютест.ОжидаетЧто(ТаблицаТоваров)
ЮТест.ОжидаетЧто(ТаблицаТоваров)
.ИмеетТип("ТаблицаЗначений")
.ИмеетДлину(5)
.Свойство("[0].Товар.Наименование").Равно("Товар 1")
@ -267,7 +110,7 @@ _Исходные данные_
.Свойство("[4].Сумма").Равно(300.5);
```
#### Загрузка области из макета
### Загрузка области из макета
Можно получить отдельную область из табличного документа и использовать ее в качестве источника данных для таблицы значений. Таким образом в одном табличном документе можно хранить несколько таблиц.
@ -288,9 +131,7 @@ _Исходные данные_
```bsl
ОписанияТипов = Новый Соответствие;
ОписанияТипов.Вставить("Товар", Новый ОписаниеТипов("СправочникСсылка.Товары"));
ОписанияТипов.Вставить("Цена", Новый ОписаниеТипов("Число"));
ОписанияТипов.Вставить("Количество", Новый ОписаниеТипов("Число"));
ОписанияТипов.Вставить("Сумма", Новый ОписаниеТипов("Число"));
// Для общего использования созданных значений используется переменная КешЗначений.
// В двух табличных документах одинаковый состав товаров.
@ -305,11 +146,11 @@ _Исходные данные_
КэшЗначений
);
ТаблицаИсходныхДанных = ЮТест.Данные().ЗагрузитьИзМакета(
"ОбщийМакет.ЮТ_МакетТестовыхДанных.R1C1R4C4",
ОписанияТипов,
КэшЗначений
);
ОписанияТипов = Новый Соответствие;
ОписанияТипов.Вставить("Товар", Новый ОписаниеТипов("СправочникСсылка.Товары"));
ОписанияТипов.Вставить("Цена", Новый ОписаниеТипов("Число"));
ОписанияТипов.Вставить("Количество", Новый ОписаниеТипов("Число"));
ОписанияТипов.Вставить("Сумма", Новый ОписаниеТипов("Число"));
ТаблицаОжидаемыхЗначений = ЮТест.Данные().ЗагрузитьИзМакета(
"ОбщийМакет.ЮТ_МакетТестовыхДанных.R5C1R8C4",
@ -319,11 +160,11 @@ _Исходные данные_
ТаблицаРезультата = ОбщийМодуль1.ТаблицаТоваровСЦенамиИзОстатков(ТаблицаИсходныхДанных);
Ютест.ОжидаетЧто(ТаблицаРезультата)
ЮТест.ОжидаетЧто(ТаблицаРезультата)
.Равно(ТаблицаОжидаемыхЗначений);
```
#### Загрузка документа с табличной частью и проверка движений
### Загрузка документа с табличной частью и проверка движений
Интеграционный-тест для проверки формирования движений документа по некоторому регистру. В первой таблице - табличная часть документа. Во второй - реквизиты самого документа. В третьей - ожидаемые движения по регистру, который нужно протестировать.
@ -331,16 +172,16 @@ _Исходные данные_
Область `Реквизиты_документа`
| ПриходТовара | Дата | Поставщик | Валюта | Склад | Организация |
|--------------|------|-------------|--------|-------|-------------|
| Документ 1 | Дата | Поставщик 1 | Валюта | Склад | Организация |
| ПриходТовара | Дата | Поставщик | Валюта | Склад | Организация |
|--------------|------|-----------|--------|-------|-------------|
| Документ 1 | Дата | Поставщик | Валюта | Склад | Организация |
Область `Табличная_часть_документа`
| Документ | Товар | Товар.Поставщик | Товар.Вид | Цена | Количество | Сумма |
|------------|---------|-----------------|-----------|-------|------------|-------|
| Документ 1 | Товар 1 | Поставщик 1 | Товар | 100 | 1 | 100 |
| Документ 1 | Товар 2 | Поставщик 1 | Товар | 2 000 | 1 | 2000 |
| Документ 1 | Товар 1 | Поставщик | Товар | 100 | 1 | 100 |
| Документ 1 | Товар 2 | Поставщик | Товар | 2 000 | 1 | 2000 |
| Документ 1 | Услуга | | Услуга | 300,5 | 1 | 300.5 |
Область `Ожидаемые_движения`
@ -354,7 +195,7 @@ _Исходные данные_
```bsl
КэшЗначений = Новый Соответствие;
Организация = Ютест.КонтекстМодуля().Организация;
Организация = ЮТест.КонтекстМодуля().Организация;
Поставщик = ЮТест.Данные().СоздатьЭлемент(Справочники.Контрагенты, "Поставщик");
ДатаДокумента = НачалоДня(ТекущаяДатаСеанса());
@ -363,7 +204,7 @@ _Исходные данные_
// значение из табличного документа к нужному значению.
ЗаменяемыеЗначения = Новый Соответствие;
ЗаменяемыеЗначения.Вставить("Организация", Организация);
ЗаменяемыеЗначения.Вставить("Поставщик 1", Поставщик);
ЗаменяемыеЗначения.Вставить("Поставщик", Поставщик);
ЗаменяемыеЗначения.Вставить("Дата", ДатаДокумента);
ОписанияТипов = Новый Соответствие;
@ -427,13 +268,13 @@ _Исходные данные_
Отбор = Новый Структура("Регистратор", ДанныеДокументов.ПриходТовара);
ОжидаемыеТоварныеЗапасы = ТаблицаДвижений.Скопировать(Отбор);
Ютест.ОжидаетЧто(ТаблицаТоварныхЗапасов)
ЮТест.ОжидаетЧто(ТаблицаТоварныхЗапасов)
.Равно(ОжидаемыеТоварныеЗапасы);
КонецЦикла;
```
#### Заменяемые значения
### Заменяемые значения
Создание и заполнение объектов двойной вложенности и более (например, "Товар.Поставщик.ВидКонтрагента") не поддерживается. Если есть такая потребность, следует использовать переменную "ЗаменяемыеЗначения". Т.е. следует создать и заполнять нужный объект перед преобразованием табличного документа в таблицу значений, а затем передать его в функцию получения таблицы значений.
@ -443,7 +284,7 @@ _Исходные данные_
.Записать();
// В табличном документе должна быть колонка [Товар.Поставщик] со значением "Некий поставщик".
// В этом случае в таблицу значений проставится значение, полученное из соответствия
// В этом случае в таблицу значений попадет значение, полученное из соответствия
ЗаменяемыеЗначения = Новый Соответствие;
ЗаменяемыеЗначения.Вставить("Некий поставщик", Поставщик);
@ -458,7 +299,7 @@ _Исходные данные_
.Свойство("[0].Товар.Поставщик.ВидКонтрагента").Равно(ВидКонтрагента);
```
#### Заменяемые значения и составные типы
### Заменяемые значения и составные типы
Можно использовать составные типы в колонках таблицы значений, но создание объектов метаданных для такой колонки не поддерживается. Можно воспользоваться соответствием "ЗаменяемыеЗначения", если потребуется в одну колонку записать значения разных типов.
@ -488,7 +329,7 @@ _Исходные данные_
;
```
#### Inline макеты, загрузка из Markdown
### Inline макеты, загрузка из Markdown
При необходимости вы можете разместить таблицу с данными прямо в коде.
@ -512,3 +353,77 @@ _Исходные данные_
Данные = ЮТест.Данные().ЗагрузитьИзМакета(ТаблицаMarkDown, ОписанияТипов);
```
## Другие методы загрузки
* [`ЮТест.Данные().ТаблицаMarkDown`](/api/ЮТТестовыеДанные#таблицаmarkdown) - Читает таблицу MarkDown в массив структур
```bsl
Макет =
"| Имя | ИмяКоллекции | Конструктор | Группы | Ссылочный | Реквизиты | Измерения | Ресурсы | РеквизитыАдресации | ТабличныеЧасти |
||------------------------|-------------------------|----------------------|--------|-----------|-----------|-----------|---------|--------------------|----------------|
|| Справочник | Справочники | СоздатьЭлемент | + | + | + | | | | + |
|| Документ | Документы | СоздатьДокумент | | + | + | | | | + |
|| ПланВидовХарактеристик | ПланыВидовХарактеристик | СоздатьЭлемент | + | + | + | | | | + |
|| ПланСчетов | ПланыСчетов | СоздатьСчет | | + | + | | | | + |
|| ПланВидовРасчета | ПланыВидовРасчета | СоздатьВидРасчета | | + | + | | | | + |
|| ПланОбмена | ПланыОбмена | СоздатьУзел | | + | + | | | | + |
|| РегистрСведений | РегистрыСведений | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрНакопления | РегистрыНакопления | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрБухгалтерии | РегистрыБухгалтерии | СоздатьНаборЗаписей | | | + | + | + | | |
|| РегистрРасчета | РегистрыРасчета | СоздатьНаборЗаписей | | | + | + | + | | |
|| БизнесПроцесс | БизнесПроцессы | СоздатьБизнесПроцесс | | + | + | | | | + |
|| Задача | Задачи | СоздатьЗадачу | | + | + | | | + | + |
|";
КоллекцияОписаний = ЮТТестовыеДанные.ТаблицаMarkDown(Макет);
ТипыМетаданных = Новый Структура();
Для Каждого Запись Из КоллекцияОписаний Цикл
Описание = Новый Структура();
Описание.Вставить("Имя", Запись.Имя);
Описание.Вставить("ИмяКоллекции", Запись.ИмяКоллекции);
Описание.Вставить("Конструктор", Запись.Конструктор);
Описание.Вставить("Группы", Запись.Группы = "+");
Описание.Вставить("Ссылочный", Запись.Ссылочный = "+");
Описание.Вставить("Реквизиты", Запись.Реквизиты = "+");
Описание.Вставить("Измерения", Запись.Измерения = "+");
Описание.Вставить("Ресурсы", Запись.Ресурсы = "+");
Описание.Вставить("РеквизитыАдресации", Запись.РеквизитыАдресации = "+");
Описание.Вставить("ТабличныеЧасти", Запись.ТабличныеЧасти = "+");
ТипыМетаданных.Вставить(Описание.Имя, Описание);
ТипыМетаданных.Вставить(Описание.ИмяКоллекции, Описание);
КонецЦикла;
Возврат ТипыМетаданных;
```
* [`ЮТест.Данные().СтруктураMarkDown`](/api/ЮТТестовыеДанные#структураmarkdown) - Читает таблицу MarkDown в структуру
```bsl
Функция ИмяКоллекции(Имя) Экспорт
Макет =
"| Имя | ИмяКоллекции |
||------------------------|-------------------------|
|| Справочник | Справочники |
|| Документ | Документы |
|| ПланВидовХарактеристик | ПланыВидовХарактеристик |
|| ПланСчетов | ПланыСчетов |
|| ПланВидовРасчета | ПланыВидовРасчета |
|| ПланОбмена | ПланыОбмена |
|| РегистрСведений | РегистрыСведений |
|| РегистрНакопления | РегистрыНакопления |
|| РегистрБухгалтерии | РегистрыБухгалтерии |
|| РегистрРасчета | РегистрыРасчета |
|| БизнесПроцесс | БизнесПроцессы |
|| Задача | Задачи |
|";
Описания = ЮТТестовыеДанные.СтруктураMarkDown("Имя", Макет);
Возврат Описания[Имя].ИмяКоллекции;
КонецФункции
```

View File

@ -0,0 +1,246 @@
---
tags: [Начало, Тестовые данные]
sidebar_position: 2
---
# Удаление тестовых данных
При использовании тестовых данных нередко необходимо удалять созданные объекты.
Для этого вы можете использовать
## Автоматические транзакции
Самый простой в способ отката изменений сделанных тестом - обернуть тест в транзакцию.
Для того чтобы YAxUnit сам оборачивал тесты в транзакции вам, при регистрации теста, нужно вызвать метод [`ВТранзакции()`](/api/ЮТТесты#втранзакции).
* Если метод вызвать в самом начале, то настройка примениться для всех тестов модуля;
* если после добавления набора, то для всех тестов этого набора;
* если после добавления теста, то только для этого теста.
* Также можно отключать использование транзакции, указав параметр `ВТранзакции(Ложь)`
```bsl title=ВТранзакции.bsl
ЮТТесты
.ДобавитьТест("Фикция").ВТранзакции() // Использование транзакции для конкретного теста
ЮТТесты
.ДобавитьТестовыйНабор("Основной").ВТранзакции() // Использование транзакции для набора тестов
.ДобавитьТест("Фикция")
ЮТТесты.ВТранзакции() // Использование транзакции для тестов модуля
.ДобавитьТест("Фикция")
```
И тогда тестовый движок будет оборачивать в транзакцию **каждый серверный тест** (для клиентских будет игнорироваться)
Особенности работы:
* В транзакцию оборачивается только тест и [события](../events.md) `ПередКаждымТестом\ПередТестом` и `ПослеКаждогоТеста\ПослеТеста`
* Для клиентских тестов настройка игнорируется
### Схема работы транзакций
Для настройки
```bsl title='Тесты набора с настройкой ВТранзакции'
ЮТТесты
.ДобавитьТестовыйНабор("Основной").ВТранзакции()
.ДобавитьТест("Тест1")
.ДобавитьТест("Тест2")
```
Схема выполнения будет выглядеть так:
```mermaid
flowchart TB
bm(ПередВсемиТестами)-->b(ПередТестовымНабором)
b-->Транзакция1
Транзакция1-->Транзакция2
Транзакция2-->a(ПослеТестовогоНабора)
a-->am(ПослеВсехТестов)
subgraph Транзакция1
direction LR
b1(ПередКаждымТестом)-->t1(Тест1)
t1-->a1(ПослеКаждогоТеста)
end
subgraph Транзакция2
b2(ПередКаждымТестом)-->t2(Тест2)
t2-->a2(ПослеКаждогоТеста)
end
```
Точно также она будет выглядеть и для настройки
```bsl
ЮТТесты.ВТранзакции()
.ДобавитьТестовыйНабор("Основной")
.ДобавитьТест("Тест1")
.ДобавитьТест("Тест2")
```
:::caution Учитывайте
В транзакцию оборачивается тест, а не модуль или набор, поэтому данные созданные вне теста не будут удалены.
Такие данные необходимо удалять самостоятельно при необходимости.
Например, настройка
```bsl
ЮТТесты
.ДобавитьТестовыйНабор("Основной").ВТранзакции() // Использование транзакции для набора тестов
```
указывает, что каждый тест набор будет выполняться в своей транзакции, которая будет отменена по окончании каждого теста.
:::
## Механизм удаления тестовых данных
В некоторых ситуация тестирование в транзакции невозможно:
* Клиентский тест
* Тестируемый метод не рассчитан на работу в транзакции (например, используются внутренние транзакции)
* Необходимо создавать данные за рамками теста (общие параметры)
В подобных случаях, для удаления созданных данных, может подойти механизм удаления тестовых данных.
Для его включения нужно при регистрации теста вызвать метод [`УдалениеТестовыхДанных()`](/api/ЮТТесты#удалениетестовыхданных).
:::caution Механизм имеет ряд ограничений и не работает для следующих кейсов:
* Данные созданы не через API
* Данные создаются внутри тестируемого кода
* Данные созданы в клиентском модуле через вызов своего серверного модуля, даже если он использует API тестового движка
:::
```bsl title=УдалениеТестовыхДанных.bsl
ЮТТесты
.ДобавитьТест("Фикция").УдалениеТестовыхДанных() // Использование для конкретного теста
ЮТТесты
.ДобавитьТестовыйНабор("Основной").УдалениеТестовыхДанных() // Использование для всех тестов набора
.ДобавитьТест("Фикция")
ЮТТесты.УдалениеТестовыхДанных() // Использование для всех тестов модуля
.ДобавитьТест("Фикция")
ЮТТесты.ВТранзакции().УдалениеТестовыхДанных() // Совместно с транзакцией
.ДобавитьТест("Фикция")
```
Механизм не использует транзакции, а запоминает в контексте все созданные с помощью API объекты и записи регистров сведений.
Позволяет:
* Удалять данные созданные с клиента
* Удалять данные созданные вне теста (в обработчиках событий)
:::caution Удаление и только
Механизм только удаляет созданные объекты, изменения не откатываются
:::
:::tip Разное время жизни данных
Механизм понимает в рамках какого контекста исполнения (тест, набор, модуль) были созданы данные и удаляет их по выходу из него.
:::
Рассмотрим время жизни объектов созданных на разных этапах тестирования, например имеем такой модуль.
```bsl title=УдалениеТестовыхДанныхВремяЖизни.bsl
Процедура ПередВсемиТестами() Экспорт
ДанныеМодуля = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура ПередТестовымНабором() Экспорт
ДанныеНабора = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура ПослеВсехТестов() Экспорт
ДанныеТеста = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура Тест() Экспорт
Ссылка = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
```
Для переменных получим разное время жизни
* `ДанныеМодуля` - Живет, пока исполняются тесты модуля одного контекста (Сервер, Клиент).
* `ДанныеНабора` - Живет, пока исполняются тесты набора.
* `ДанныеТеста` и `Ссылка` - Живут, пока исполняется тест.
### Схема работы механизма "УдалениеТестовыхДанных"
```mermaid
sequenceDiagram
actor Y as YAxUnit
participant M as Тестовый модуль
participant D as Тестовые данные
box rgb(132 132 132 / 10%) Контексты
participant MC as КонтекстМодуля
participant SC as КонтекстНабора
participant TC as КонтекстТеста
end
Y->>M: ПередВсемиТестами
activate M
M->>D: СоздатьЭлемент
activate D
D ->> MC: ЗапомнитьСсылку
D ->> M: Созданный элемент
deactivate D
deactivate M
Y->>M: ПередТестовымНабором
activate M
M->>D: СоздатьЭлемент
activate D
D ->> SC: ЗапомнитьСсылку
D ->> M: Созданный элемент
deactivate D
deactivate M
Y ->> M: Выполнить тест
activate M
M ->> D: КонструкторОбъекта().Записать()
activate D
D ->> TC: ЗапомнитьСсылку
D ->> M: Созданный элемент
deactivate D
deactivate M
Y->>M: ПослеТеста
activate M
M->>D: УдалитьТестовыеДанные
activate D
D ->> TC: ПолучитьСохраненныеСсылки
TC ->>D: Сохраненные cсылки
D-->>D: Удаление сохраненных ссылок
deactivate D
deactivate M
Y->>M: ПослеТестовогоНабора
activate M
M->>D: УдалитьТестовыеДанные
activate D
D ->> SC: ПолучитьСохраненныеСсылки
SC ->>D: Сохраненные cсылки
D-->>D: Удаление сохраненных ссылок
deactivate D
deactivate M
Y->>M: ПослеВсехТестов
activate M
M->>D: УдалитьТестовыеДанные
activate D
D ->> MC: ПолучитьСохраненныеСсылки
MC ->>D: Сохраненные cсылки
D-->>D: Удаление сохраненных ссылок
deactivate D
deactivate M
```
## Глобальная настройка удаления данных
Для глобальной настройки [`ВТранзакции()`](/api/ЮТТесты#втранзакции) и/или [`УдалениеТестовыхДанных()`](/api/ЮТТесты#удалениетестовыхданных) вам необходимо всего лишь создать свой общий модуль, подписать на событие инициализации и там установить глобальные настройки.
Подробнее тема будет раскрыта в будущем.

View File

@ -0,0 +1,127 @@
---
tags: [Начало, Тестовые данные]
sidebar_position: 2
---
# Тестовые данные
Тестовые данные, на мой взгляд, одна из самых сложных тем в тестировании решений на базе 1С:Предприятия.
Почти все алгоритмы системы опираются на данные базы, они их создают, обрабатывают, изменяют и используют в логике работы. Для корректного проведения одного документа может понадобиться множество записей справочников, настройки/остатки из регистров, значения констант и так далее.
Все это вам придется создать в тесте, который проверит корректность проведения этого документа. И это будет проверка только одного кейса. Для проверки проведения в других условиях вам придется создать еще пачку данных и так далее.
Можно сократить объемы создаваемых данных используя [мокито](../mocking/mockito.md) и отключая часть логики для каких-то кейсов, но все равно данных понадобится много.
Для облегчения работы с данными вам необходимо продумать схему, как вы будете с ними работать, откуда брать, как создавать, искать и так далее.
## Подходы к работе с данными
1. Ручное наполнение тестовой базы (мы предварительно создаем все что необходимо для тестов в базе, а потом используем её для прогона тестов)
2. Использование файлов с данными (макеты - сгенерированные в нужном формате файлы с описанием тестовых данных, загружаемые при выполнении тестов)
3. Программное создание нужных данных внутри теста
У каждого из вариантов есть свои плюсы и минусы и при грамотной компоновке можно достичь наилучшего результата.
* Ручное наполнение базы не учитывает последующие доработки системы, нужна миграция на новые алгоритмы и структуру. А также в этом случае нам необходимо завязываться на конкретные записи в системе, и при их изменении тестом мы можем сломать другие тесты. Поэтому, предварительное наполнение базы должно быть минимальным.
* В случае использования макетов данных возникают некоторые трудности с их доработкой и вариативностью, например, в новом тесте нужны данные из макета, но с некоторыми изменениями. В этом случае нам потребуется создавать новый макет или проверять, не сломают ли наши изменения другие тесты. Также при таком подходе сложно контролировать и искать какие макеты используются и кем.
* Программное создание тестовых данных увеличивает время и сложность теста.
Если грамотно распределить тестовые данные на способы создания, можно добиться оптимального результата.
Например, статичные данные создать вручную, сложные кейсы данных засунуть в макеты, а для программного создания реализовать методы-конструкторы, которые будем переиспользовать. Таким образом, сможем уйти от минусов того или иного подхода или минимизировать их вред.
Ниже хочу рассказать как мы работает с данными.
### Образ тестовой базы
Для прогона тестов используется предварительно подготовленный образ базы, на котором гоняются тесты как в CI, так и на базах разработки.
Образ базы - это выгрузка (файл dt) или сама база (файл 1cd), на основании которого копированием или загрузкой создается база для прогона тестов.
### Ручное наполнение тестовой базы
Образ предварительно настраивается вручную (либо методами обновления/миграции), вводится минимально необходимая информация и базовые настройки.
В образе не должно быть документов, справочников и прочего, с чем обычно имеют дело пользователи, только минимальный набор неизменных данных.
### Программное создание данных
Все остальные данные тесты готовят для себя сами.
В большинстве случаев используется программное создание данных
* Данные создаются кодом, используя [механизмы движка](data-generation.md)
* Используется генерация случайных значений, где это не противоречит требования
* Тесты стараются [прибрать за собой](test-data-deletion.md), используя транзакции или механизм удаления данных
* Созданы отдельные [модули-помощники](../../getting-started/auxiliary-modules.md) реализующие типовые кейсы данных
#### Благодаря программной генерации данных вы получаете
* Старый добрый код 1С, который понятен и не требует изучения дополнительных материалов
* Переиспользование и кастомизация алгоритмов генерации данных - общие модули, экспортные методы, параметры.
* Возможность создавать новые уникальные данные
* Можете использовать уже готовую логику тестируемого решения
```bsl title="Общий модуль ТестовыеДанные"
Функция НовыйТовар() Экспорт
// Логика генерации
КонецФункции
Функция НовыйПоставщик() Экспорт
// Логика генерации
КонецФункции
Функция НовоеПоступлениеТовара(Склад, Знач Товар = Неопределено, Знач Поставщик = Неопределено) Экспорт
Если Поставщик = Неопределено Тогда
Поставщик = НовыйПоставщик();
КонецЕсли;
Если Товар = Неопределено тогда
Товар = НовыйТовар();
КонецЕсли;
Возврат ЮТест.Данные().КонструкторОбъекта(Документы.ПоступлениеТоваров)
.Установить("Склад", Склад)
.Установить("Поставщик", Поставщик)
.ФикцияОбязательныхПолей();
.ТабличнаяЧасть("Товары")
.ДобавитьСтроку()
.Установить("Номенклатура", Товар)
.Фикция("Количество")
.Фикция("Цена")
.Провести();
КонецФункции
```
#### Использование генерации случайных значений позволяет
* Сократить количество кода
* Акцентировать внимание на "важных" значениях
Например, в тесте ниже важно, чтобы `товар` и `склад` в документах совпадали, дата расхода должна быть больше прихода, количество расхода меньше прихода.
```bsl title="Тест"
Склад = ТестовыеДанные.НовыйСклад();
Товар = ТестовыеДанные.НовыйТовар();
ДатаПоступления = ЮТест.Данные().СлучайнаяДата();
ДатаРасхода = ЮТест.Данные().СлучайнаяДатаПосле(ДатаПоступления);
КоличествоРасход = ЮТест.Данные().СлучайноеПоложительноеЧисло(100);
КоличествоПоступления = КоличествоРасход + ЮТест.Данные().СлучайноеПоложительноеЧисло(100);
ТестовыеДанные.НовоеПоступлениеТовара(Склад, Товар, КоличествоПоступления, ДатаПоступления);
ТестовыеДанные.НовыйРасходТовара(Склад, Товар, КоличествоРасход, ДатаРасхода);
// Проверки
```
* Вскрыть неожиданные проблемы, за счет случайности данных вы покрываете больше кейсов. Правда это имеет и негативный эффект - тесты могут "фонить", то падать, то успешно проходить, а также сложнее воспроизвести упавший тест.
Правильно организовав работу с тестовыми данными вы существенно облегчите процесс создания и поддержки тестов
* Регламентируйте подход к именованию модулей и методов, это облегчит их поиск
* Договоритесь как вы будете создавать общие модули, как разделяются по ним конструкторы, например:
* Использовать один модуль на все методы-конструкторы, подходит для небольших решений.
* Создаете модули по подсистемам (разрезы учета/функциональности) - для небольших и средних конфигураций.
* Для каждого объекта/функциональности создается свой модуль с конструкторами - для больших и сложных проектов.
* Выполняйте перекрестные ревью, делитесь знаниями в команде.

View File

@ -0,0 +1,52 @@
---
tags: [Начало]
sidebar_position: 0
---
# Регистрация тестовых методов
Кроме того, чтобы написать тестовые сценарии (методы), разработчик должен зарегистрировать их в движке - рассказать ему, какие имеются тесты, как их запускать, с какими параметрами и т.д.
Регистрация выполняется внутри предопределенного экспортного метод `ИсполняемыеСценарии`, который обязательно должен находиться в каждом тестовом модуле.
```bsl
Процедура ИсполняемыеСценарии() Экспорт
// Регистрация тестов
ЮТТесты // Регистрация тестов выполняет через модуль регистратор
.ДобавитьТестовыйНабор("Математические методы") // Набор - объединение тестов
.ДобавитьТест("Сложение") // Обязательно при регистрации указываем имя экспортного метода
.ДобавитьТест("Вычитание", "Вычитание") // Также можно указать представление теста
.ДобавитьТест("Вычитание", "ВычитаниеСервер", , "Сервер") // Контекст исполнения, по умолчанию тест выполняется во всех контекстах модуля
.ДобавитьКлиентскийТест("УмножениеНаКлиенте") // Есть отдельный метод для регистрации клиентских тестов
.ДобавитьСерверныйТест("ДелениеНаСервере", "Деление на сервер") // Есть отдельный метод для регистрации серверных тестов
.ДобавитьТестовыйНабор("Строковые методы")
.ДобавитьТест("СтрНайти")
.ДобавитьТест("СтрРазделить");
КонецПроцедуры
Процедура Сложение() Экспорт
// Тест сложения
КонецПроцедуры
```
В нем перечисляются все доступные тесты этого модуля.
Метод `ИсполняемыеСценарии` вызывается движком для каждого тестового модуля при загрузке тестов, задолго до исполнения самих тестов.
В нем **не стоит** создавать какие-то тестовые данные или выполнять инициализацию, он должен просто отдать список тестов модуля.
Регистрация и настройка тестов выполняется через методы модуля [`ЮТТесты`](/api/ЮТТесты), он позволяет:
* Зарегистрировать тесты, с возможностью выбрать контексты исполнения:
* [`ДобавитьТест`](/api/ЮТТесты#добавитьтест) - Тест выполняется во всех контекстах тестового модуля
* [`ДобавитьКлиентскийТест`](/api/ЮТТесты#добавитьклиентскийтест) - Тест выполняется на клиенте
* [`ДобавитьСерверныйТест`](/api/ЮТТесты#добавитьсерверныйтест) - Тест выполняется на сервере
* [`ДобавитьТестовыйНабор`](/api/ЮТТесты#добавитьтестовыйнабор) позволяет сгруппировать тесты в наборы
* Настроить параметры исполнения теста
* [Очистка данных](test-data/test-data-deletion), перед использованием изучите [документацию](test-data/test-data-deletion)
* Выполнение [`ВТранзакции`](/api/ЮТТесты#втранзакции) - Тест выполняется в транзакции
* Удаление тестовых данных ([`УдалениеТестовыхДанных`](/api/ЮТТесты#удалениетестовыхданных)) - Данные созданные тестом будут удалены (если они создаются с помощью API движка - [`ЮТест.Данные()`](/api/ЮТест#данные)) вне зависимости от транзакции
* [`СПараметрами`](/api/ЮТТесты#спараметрами) - Тестовый метод будет вызван с указанными параметрами
* Настройка обработчиков [событий](events.md)
* [`Перед`](/api/ЮТТесты#перед) - Указывает, какой метод будет выполнятся перед тестом
* [`После`](/api/ЮТТесты#после) - Указывает, какой метод будет выполнятся после тестом

View File

@ -1,32 +0,0 @@
---
sidebar_position: 2
tags: [Начало]
---
# Пишем первый тест
Для создания теста нужно в расширении (в отдельном или в том же) добавить модуль, содержащий экспортный метод регистрации - ИсполняемыеСценарии и реализовать тесты.
```bsl title="ОМ_ПервыйТест"
#Область СлужебныйПрограммныйИнтерфейс
Процедура ИсполняемыеСценарии() Экспорт
ЮТТесты
.ДобавитьТест("Сложение")
.СПараметрами(2, 3, 5)
.СПараметрами(2, -3, -1)
;
КонецПроцедуры
Процедура Сложение(ПервыйОперанд, ВторойОперанд, Результат) Экспорт
ЮТест.ОжидаетЧто(ПервыйОперанд + ВторойОперанд)
.ИмеетТип("Число")
.Равно(Результат);
КонецПроцедуры
#КонецОбласти
```

View File

@ -0,0 +1,97 @@
# Модули помощники
При написании тестов не стоит забывать, что вы пишете код, а это значит, что вы можете использовать те же принципы, что при обычной разработке.
Вы можете создавать общие модули и экспортные методы, которые упростят написание тестов для вашего продукта.
Такие как модули создания тестовых данных, настройки мокирования, общих проверок и так далее.
Главное сначала подумать об организации работы с ними, чтобы в будущем было проще находить нужные методы и избежать конфликтов при разработке.
## Конструкторы данных
Предположим у нас есть подсистема управления складом, которая содержит документы поступления, перемещения и реализации.
Создание документа поступления нам понадобится при тестировании всех видов документов, поэтому стоит вынести это в отдельный общий модуль.
```bsl title="ОбщийМодуль.ТестовыеДанныеУправлениеСкладом"
Функция НовоеПоступлениеТовара(Склад, Товар, Поставщик = Неопределено) Экспорт
Конструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПоступлениеТоваров)
.ФикцияОбязательныхПолей()
.Установить("Склад");
Если Поставщик = Неопределено Тогда
Конструктор.Фикция("Поставщик");
Иначе
Конструктор.Установить("Поставщик", Поставщик);
КонецЕсли
Возврат Конструктор
.ТабличнаяЧасть("Товары")
.ДобавитьСтроку()
.Установить("Номенклатура", Товар)
.Фикция("Количество")
.Фикция("Цена")
.Записать();
КонецФункции
```
Бывают случаи, когда слишком сложно кастомизировать создание объекта через параметры, тогда удобнее возвращать конструктор объекта с базовым заполнением. Либо сделать набор методов-пресетов для различных кейсов.
```bsl title="ОбщийМодуль.ТестовыеДанныеУправлениеСкладом"
Функция КонструкторПоступленияТовара(Склад, Поставщик = Неопределено) Экспорт
Конструктор = ЮТест.Данные().КонструкторОбъекта(Документы.ПоступлениеТоваров)
.ФикцияОбязательныхПолей()
.Установить("Склад");
Если Поставщик = Неопределено Тогда
Конструктор.Фикция("Поставщик");
Иначе
Конструктор.Установить("Поставщик", Поставщик);
КонецЕсли
Возврат Конструктор;
КонецФункции
Функция НовоеПоступлениеТовара(Склад, Товар, Поставщик = Неопределено) Экспорт
Конструктор = КонструкторПоступленияТовара(Склад, Поставщик);
Возврат Конструктор
.ТабличнаяЧасть("Товары")
.ДобавитьСтроку()
.Установить("Номенклатура", Товар)
.Фикция("Количество")
.Фикция("Цена")
.Записать();
КонецФункции
Функция НовоеПоступлениеТовараОтЮрЛица(Склад, Товар) Экспорт
Поставщик = НовоеЮрЛицо();
Конструктор = КонструкторПоступленияТовара(Склад, Поставщик);
Возврат Конструктор
.ТабличнаяЧасть("Товары")
.ДобавитьСтроку()
.Установить("Номенклатура", Товар)
.Фикция("Количество")
.Фикция("Цена")
.Записать();
КонецФункции
Функция НовоеПоступлениеТовараОтФизЛица(Склад, Товар) Экспорт
Поставщик = НовоеФизЛицо();
Конструктор = КонструкторПоступленияТовара(Склад, Поставщик);
Возврат Конструктор
.ТабличнаяЧасть("Товары")
.ДобавитьСтроку()
.Установить("Номенклатура", Товар)
.Фикция("Количество")
.Фикция("Цена")
.Записать();
КонецФункции
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -0,0 +1,58 @@
---
sidebar_position: 1
tags: [Начало]
---
# Плагин для EDT
Предоставляет следующие функции
## Запуск тестов
В первую очередь, плагин предоставляет новую конфигурацию запуска, позволяя указать настройки запуска тестов.
![Расширение и конфигурация запуска](images/launch-type-and-extension.png)
После создания конфигурация запуска тестов, появляется возможность запустить выполнение тестов, используя стандартное меню EDT _(также как и 1С:Предприятие)_.
Кроме конфигурации запуска тестов, плагин добавляет команды запуска тестова для конкретных методов, а также тестовых модулей.
Справа от имени каждого тестового метода появляется кнопка запуска, запускающая этот тест или все тесты модуля _(если выполнить запуск тестова для метода `ИсполняемыеСценарии`)_.
Для удобства, эти команды продублированы в контекстное меню на панеле "Схема модуля".
![Команды запуска теста](images/module-launch-test.png)
## Отчет о тестировании
По окончании тестирования плагин выводит отчет в котором детально отображается список пройденных тестов, статус их прохождения и информация о возникших ошибках.
![Отчет](images/report.png)
Отчет позволяет:
* Получить информацию о результате тестирования;
* Перезапустить тесты _(упавшие, все или выбранные)_;
* Посмотреть историю тестовых прогонов;
* Перейти к тесту и тестируемому методу;
* Просмотреть стек ошибки и перейти к месту их возникновения;
* Увидеть различие ожидаемого и фактического значения.
## Команды помощники
Для помощи при написании тестов добавлена команда "1С:Модульные тесты (YAxUnit)" в контекстное меню редактора и схемы модуля. Она позволяет:
* создавать тестовые методы,
* генерировать наборы тестов,
* создавать моки,
* переходить между проверяемым методом и тестом,
* а также запускать тесты.
При выборе этой команды открывается окно с доступными действиями. Список действий формируется динамически и зависит от модуля.
Для модулей конфигурации доступны действия создания тестов и моков, а также переходы к тестам при их наличии.
Для модулей с тестами - команды создания тестов и их запуска.
Для того, чтобы движок мог находить тестовые модули, выполнять переходы между ними, вы должны следовать [схеме наименования](../structure.md)
![Команды переходов](images/yaxunit-commands.png)

View File

@ -0,0 +1,125 @@
---
sidebar_position: 2
tags: [Начало]
---
# Пишем первый тест
## Подготовка окружения
Перед написанием тестов вам необходимо:
* Создать информационную базу, на которой будете прогонять тесты.
* Установить [тестовый движок](install/install.md) в проект. Не забывайте о необходимости сбросить признаки `Безопасный режим` и `Защита от опасных действий` после загрузки расширения.
* Если вы используете 1С:Enterprise Development Tools (EDT), то установить [плагин](install/install-plugin.md), который будет вам помогать.
## Первый тест
Вы все установили, перед вами открыта EDT или конфигуратор, в котором есть проверяемая конфигурация и расширение с тестовым движком `YAxUnit`.
Тесты с использование YAxUnit пишутся в расширении, это может быть как отдельное расширение, так и расширение движка. Подробнее об плюсах и минусах этих вариантов вы можете почитать в [статье об организации тестов](structure.md).
Для первого теста **рекомендую** использовать расширение движка.
Необходимо создать общий модуль в расширении (новый, а не заимствованный), назовем его `ОМ_ПервыйТест`, это будет наш первый тестовый набор (test suite).
Следующим шагом необходим создать экспортную процедуру с наименованием `ИсполняемыеСценарии`, это главный метод тестового набора, здесь необходимо будет перечислить тесты, которые содержит модуль. Так как в 1С:Предприятии нельзя в режиме исполнения понять, какие методы содержит модуль (нет рефлексии).
```bsl title="ОМ_ПервыйТест"
Процедура ИсполняемыеСценарии() Экспорт
КонецПроцедуры
```
Предположим **мы хотим протестировать** как работает сложение.
Для этого нам необходимо создать **экспортный метод**, который будет реализовывать тест и **зарегистрировать** его в методе `ИсполняемыеСценарии`.
```bsl title="ОМ_ПервыйТест"
Процедура ИсполняемыеСценарии() Экспорт
ЮТТесты.ДобавитьТест("Сложение");
КонецПроцедуры
Процедура Сложение() Экспорт
КонецПроцедуры
```
За [регистрацию тестов](../features/test-registration.md) отвечает модуль `ЮТТесты`. Он содержит набор методов позволяющих добавлять тесты и настраивать их исполнение (параметры, режимы исполнения - клиент/сервер, работу в транзакции и многое другое).
С помощью команды `ЮТТесты.ДобавитьТест("Сложение")` мы добавляем тест `Сложение` в тестовый набор. На этом этапе можно уже [запускать](run/run.md) наш тест.
Теперь добавим полезной нагрузки тесту, проверим что `2 + 2 = 4`.
```bsl title="ОМ_ПервыйТест"
Процедура Сложение() Экспорт
РезультатСложения = 2 + 2; // 1
ЮТест.ОжидаетЧто(РезультатСложения) // 2
.Равно(4); // 3
КонецПроцедуры
```
1. Тест выполняет интересующую нас команду `2 + 2` (это может быть вызов какого-то метода конфигурации).
2. Используя [механизм утверждений](../features/assertions/assertions.md) (проверок) указываем, что хотим проверить `ЮТест.ОжидаетЧто(РезультатСложения)`
3. Проверяем, что `РезультатСложения` равно `4`
С помощью утверждений разработчик описывает, ожидания (требования) от работы команды(метода). Тест во время исполнения, проверяет соответствуют ли эти ожидания реальным данным, если есть расхождения, то будет выброшено исключение, которое зафиксируется в отчете о тестировании.
Настало время запустить наш первый тест, как это сделать прочтите в [статье](run/run.md).
После выполнения вы увидите отчет о тестировании, в котором тест будет "зеленым". Если в тесте исправить проверку `.Равно(4);` на `.Равно(3);` и еще раз запустить, то тест уже изменит свой статус и вы увидите сообщение об ошибке.
Можно еще доработать нащ тест, проверить разные варианты сложения, для этого могут помочь параметры теста.
```bsl title="ОМ_ПервыйТест"
Процедура ИсполняемыеСценарии() Экспорт
ЮТТесты
.ДобавитьТест("Сложение")
.СПараметрами(2, 3, 5)
.СПараметрами(2, -3, -1)
.СПараметрами("2", "-3", "2-3")
.СПараметрами("2", -3, "2-3")
;
КонецПроцедуры
Процедура Сложение(ПервыйОперанд, ВторойОперанд, Результат) Экспорт
РезультатСложения = ПервыйОперанд + ВторойОперанд;
ЮТест.ОжидаетЧто(РезультатСложения)
.Равно(Результат);
КонецПроцедуры
```
При регистрации указываются параметры, с которыми должен быть вызван тест, дополняем сигнатуру процедуры, чтобы она приняла параметры и переводим тест на использование этих параметров.
Также можно добавить проверок.
```bsl title="ОМ_ПервыйТест"
Процедура Сложение(ПервыйОперанд, ВторойОперанд, Результат) Экспорт
РезультатСложения = ПервыйОперанд + ВторойОперанд;
ЮТест.ОжидаетЧто(РезультатСложения)
.Заполнено()
.ИмеетТип("Число, Строка")
.Равно(Результат);
КонецПроцедуры
```
Поздравляю с первый реализованным тестом.
Используя [руководство](../features) и имеющиеся тесты вы сможете дальше изучить и написать более полезные тесты для своих продуктов.
Примеры тестов:
* [Тестов движка](https://github.com/bia-technologies/yaxunit/tree/develop/tests/src/CommonModules)
* Тесты других открытых проектов
* [bellerage-ssl](https://github.com/Bellerage-IT/bellerage-ssl/tree/master/src/cfe/yaxunit/src/CommonModules)
* [AdvancedGlobalSearchOneS](https://github.com/SeiOkami/AdvancedGlobalSearchOneS/tree/main/%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9%D0%93%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9%D0%9F%D0%BE%D0%B8%D1%81%D0%BA.%D0%A2%D0%B5%D1%81%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5/src/CommonModules)
Также есть [видео курс](https://www.youtube.com/playlist?list=PLQ0oWbrgHNk5RF3dfH1QexQLr38Cm03Kj), кажется годным, но все не смотрел.

View File

@ -0,0 +1,102 @@
# Текучий интерфейс
Большая часть публичной функциональности тестового движка 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
* не захламлять контекстную подсказку свойствами обработки (или формы для клиента)
* оптимизировать время работы (нет затрат на создание обработок)
Дополнительно, так как контекст глобальный, не обязательно передавать объект, чтобы получить доступ к настроенному контексту.
Например:
* При регистрации теста, информация о тестах никуда не возвращается, движок, после вызова метода `ИсполняемыеСценарии`, просто считывает результат из глобального контекста.
* Или настройки мокито, выполненные в методе теста сразу же доступны в методах других модулей (перехватываемых).

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

View File

@ -0,0 +1,14 @@
---
sidebar_label: "Начало работы"
sidebar_position: 0
---
# Начало работы
YAxUnit - это расширение конфигурации с открытым исходным кодом, которое помогает в нелегком труде по написанию модульных тестов для решений на платформе 1С:Предприятие.
![Отчет о тестировании](../images/report-ui.png)
Прежде всего нужно пройти через [процесс установки](install/).
После этого вы можете попробовать написать свой [первый тест](first-test.md) или ознакомиться с возможностями YAxUnit изучив [руководство](../features/).

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -1,8 +1,8 @@
---
sidebar_position: 2
sidebar_label: Установка плагина EDT
sidebar_position: 1
sidebar_label: Установка плагина для EDT
---
# Установка плагина для работы с тестами в EDT
# Установка плагина для работы с тестами
```mdx-code-block
import Tabs from '@theme/Tabs';
@ -10,7 +10,7 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
Для того, что бы вам и нам было проще писать тесты мы разработали плагин для EDT.
Для того, что бы вам и нам было проще писать тесты мы разработали плагин для 1С:Enterprise Development Tools (EDT).
С его помощью вы сможете:
@ -20,40 +20,34 @@ import TabItem from '@theme/TabItem';
Для установки плагина вам необходимо воспользоваться стандартным функционалом Eclipse (EDT) `Установить новое ПО...`.
* Открываем EDT.
* Открываем EDT.
* Переходим к `Установить новое ПО` (в меню `Справка`);
* Добавляем новый репозиторий;
```mdx-code-block
<Tabs>
<TabItem value="master" label="Основной репозиторий">
```
````mdx-code-block
<Tabs>
<TabItem value="master" label="Основной репозиторий">
```url
https://bia-technologies.github.io/edt-test-runner/repository
```
```mdx-code-block
</TabItem>
<TabItem value="develop" label="Develop">
```
</TabItem>
<TabItem value="develop" label="Develop">
```url
https://bia-technologies.github.io/edt-test-runner/dev/repository
```
```mdx-code-block
</TabItem>
</Tabs>
</TabItem>
<TabItem value="old-2023-02" label="Для версий EDT до 2023-02">
```url
https://bia-technologies.github.io/edt-test-runner/repository/updates/23.x
```
</TabItem>
</Tabs>
````
![Установка плагина](images/plugin-install-from-rep.png)
:::tip совет
Для ускорения установки можно убрать галочку `Обращаться во время инсталляции ко всем сайтам ...`
:::
* Нажимаем далее;
@ -61,7 +55,5 @@ import TabItem from '@theme/TabItem';
* Соглашаемся с предупреждением безопасности;
* И перезагружаем IDE.
:::note примечание
EDT будет долго перезагружаться и это нормально.
:::

View File

@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 0
tags: [Начало]
---
@ -11,16 +11,16 @@ YAxUnit - это расширение для 1С:Предприятия, кот
* 1С:Предприятие версии 8.3.10 или старше
## Установка в EDT
## Установка в 1С:Enterprise Development Tools (EDT)
### Первичная установка тестового движка в рабочее пространство (workspace)
1. Скачаем архив [релиза](https://github.com/bia-technologies/yaxunit/releases/latest)
![Страница релиза](images/gh-release.png)
2. Копируем из архива расширение расположенное в каталоге `exts/yaxunit/` в свой каталог с исходниками (не в воркспейс)
2. Копируем из архива расширение расположенное в каталоге `exts/yaxunit/` в свой каталог с исходниками (не в workspace)
![Содержимое архива](images/gh-release-content.png)
3. Импортируем проект расширения в воркспейс
3. Импортируем проект расширения в workspace
![Импорт проекта](images/project-import.png)
4. Привязываем импортированный проект расширения к конфигурации
![Связь с базовым проектом](images/link-base-project.png)

View File

@ -0,0 +1,12 @@
# Рекомендации
* Для того чтобы лучше понять тестирование и научиться разным приемам читайте/ищите статьи по тестированию в других языках/продуктах. Большинство практик и примеров универсальные и никак не связаны с тем, что вы тестируете, разница лишь в инструментарии.
* Перед внедрением, когда вы уже попробовали и поняли необходимость использования тестов вам следует сразу же продумать и регламентировать
* Подход к организации тестов - что тестировать, что нет, как называть модули с тестами, как именовать модули помощники. Это все нужно чтобы упростить работу с тестами в будущем.
* Как вам валидировать "правильность" тестов, чтобы они были действительно полезными.
## Внедрение
* Если у вас в компании множество команд занимающихся тестированием, то вам следует:
* Продумать схему обновления, рекомендую использовать релизный репозиторий и обновление через git
* Возможно стоит создать свою версию движка на базе публичной версии, в которой вы будете собирать "помогаторы" тестирования для ваших продуктов.

View File

@ -28,12 +28,12 @@
## Параметры логирования
| Имя параметра | Тип | Значение по умолчанию | Описание |
|---------------|-----------|-----------------------|-----------------------------------------------------------------------------------------------------------------------------|
| `file` | `String` | `""` | Путь к файлу лога |
| Имя параметра | Тип | Значение по умолчанию | Описание |
|---------------|-----------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------|
| `file` | `String` | `""` | Путь к файлу лога |
| `enable` | `Boolean` | `Null` | Использование логирвания.<br/>Если не указан, зависит от параметра `file` и `console`, если установлены - вкл, если нет - выкл |
| `console` | `Boolean` | `false` | Вывод лога в stdout (консоль) |
| `level` | `String` | `"debug"` | Уровень детализации лога. Возможные значения: `"debug"`, `"info"`, `"error"` |
| `console` | `Boolean` | `false` | Вывод лога в stdout (консоль) |
| `level` | `String` | `"debug"` | Уровень детализации лога. Возможные значения: `"debug"`, `"info"`, `"error"` |
Примеры:

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,91 @@
---
sidebar_position: 3
tags: [Начало]
---
# Запуск и отладка тестов
В общем случае запуск тестов выполняется запуском 1С:Предприятия с параметром `RunUnitTests`. При необходимости можно указать путь к файлу [конфигурации запуска](configuration.md) для настройки прогона тестов.
Для упрощения этого процесса есть другие способы.
:::caution Важно
*После загрузки расширения в информационную базу необходимо отключить у него `безопасный режим` и `защиту от опасных действий`*
:::
## Запуск из 1С:Enterprise Development Tools (EDT)
Если вы используете EDT, то установив [плагин](../install/install-plugin.md) вы получаете возможность запускать тесты нажатием одной кнопки.
:::info
Все описанные ниже способы позволяют запускать тесты в режиме отладки
:::
### Конфигурация запуска
Плагин добавляет в EDT новый тип конфигурации запуска приложения.
![EDT](images/run-configuration.png)
С ее помощью вы можете запускать тесты также как и 1С:Предприятие (кнопкой панели инструментов)
![EDT](images/run-button.png)
:::caution Важно
Без созданной и настроенной конфигурации запуска модульных тестов не будут работать другие варианты запуска.
:::
### Запуск из боковой панели модуля
Также плагин позволяет запускать тесты прямо из модуля для нужных методов.
Если вы перейдете в модуль с тестами, то рядом с каждым тестовым методом вы увидите иконки запуска.
![EDT](images/run-from-module.png)
Кликнув по ним вы сможете запустить конкретный тест.
При нажатии на иконку метода `ИсполняемыеСценарии` вы запустите все тесты модуля.
Команды запуска добавлены в схему модуля.
![EDT](images/run-from-schema.png)
### Запуск из палитры команд
Плагин добавляет набор различных команд для работы с тестами, среди которых есть и запуск.
![EDT](images/run-from-command-palette.png)
Основным ее плюсом является возможность работать с клавиатуры и не запоминать большое количество горячих клавиш.
`Ctrl+Shift+T`, затем `Enter` и тест текущего метода запущен.
## Запуск из конфигуратора
Возможности конфигуратора не столь большие, поэтому есть только один способ запустить тесты.
1. Создать файл конфигурации запуска
* [Вручную](configuration.md)
* С помощью [формы настройки](../../yaxunit-ui.md#интерфейс-настройки-конфигурации)
2. Указать путь к файлу конфигурации запуска в параметрах запуска
![Конфигуратор](images/from-configurator.png)
:::tip
При разработки/отладке тестов полезно держать открытой [форму настройки](../../yaxunit-ui.md#интерфейс-настройки-конфигурации) для быстрой перенастройки запускаемых тестов. Сначала запустил тесты модуля, потом в той же форме убрали "зеленые", сохранили настройку в тот же файл и выполнили отладку нужных тестов, запустив их из конфигуратор.
:::
## Запуск из предприятия
Для запуска тестов из предприятия необходимо воспользоваться командой [Запуск тестирования](../../yaxunit-ui.md#запуск-тестов)
![Запуск тестов](../../images/ui-run-tests.png)
## Строка запуска предприятия
`[путь к клиенту 1С] ENTERPRISE [Параметры подключения к ИБ] [Параметры авторизации] /C RunUnitTests=/путь/к/конфигурационному/файлу`
Для формирования строки запуска можно воспользоваться [интерфейсом настройки](../../yaxunit-ui.md#интерфейс-настройки-конфигурации)
Пример:
`"C:\Program Files\1cv8\8.3.18.1698\bin\1cv8c.exe" ENTERPRISE /IBName MyInfoBase /N Admin /C RunUnitTests=C:\tmp\test-config.json`
Запуск тестов в режиме тонкого клиента на информационной базе `MyInfoBase` под пользователем `Admin` по конфигурации указанной в файле `C:\tmp\test-config.json`

View File

@ -0,0 +1,79 @@
# Организация тестов
## Расширение с тестами
Тесты размещаются в расширениях конфигурации, вы можете их совместить с движком, либо создать отдельное расширение.
* Вместе с движком.
* Доступна контекстная подсказка.
* Сложнее обновлять движок, необходимо воспользоваться сравнением-объединением.
* В отдельном расширении.
* Нет подсказки по методам движка.
* Движок обновляется загрузкой/заменой.
* Проще переключаться между версиями.
* Движок можно не хранить в репозитории проекта.
Мы в своих проектах используем первый вариант, так как удобство и экономия времени каждым разработчиком от контекстной подсказки перевешивает минус ручного обновления.
![Тесты движка](images/structure.png)
## Тестовые модули
Тесты располагаются в общих модулях (не заимствованных).
Тестовые модули (наборы тестов объекта) состоят из следующих блоков:
* Метод регистрации тестов `ИсполняемыеСценарии`.
В методе регистрации необходимо [перечислить все тесты](../features/test-registration.md) и, при необходимости, выполнить настройку.
* Реализация тестов, тестовые методы, собственно сами тесты.
* [Обработчики событий](../features/events.md), при необходимости.
Принято на один тестируемый модуль объекта создавать один общий модуль с тестами, например, тесты на модуль объекта и отдельно тесты на модуль менеджера.
Также полезно использовать регламент наименования тестовых модулей, так чтобы вам проще было с ними работать в будущем, облегчить навигацию.
Рекомендуем рассмотреть схему именования для EDT и если ее соблюдать, вы сможете с легкостью переключатся между тестом и тестируемым методом.
### Схема наименования модулей
Все тесты должны располагаться в общих модулях.
Имя тестового модуля должно соответствовать шаблону `[Префикс типа объекта_][Имя проверяемого объект]{_Суффикс типа модуля}`, где суффикс не обязателен, но крайне желателен.
По правильно названному модулю мы можем с легкостью получить информацию о типе тестируемого объекта и его имени, а также тип его модуля, методы которого проверяем. Список поддерживаемых суффиксов и префиксов приведен в таблицах ниже.
#### Префиксы типов объекта
| Тип тестируемого объекта | Префикс | Пример |
| ------------------------ | ------- | ------------------------------------------- |
| Общий модуль | `ОМ_` | ОМ_ОбщегоНазначения |
| Регистр бухгалтерии | `РБ_` | РБ_Хозрасчетный, РБ_Хозрасчетный_НЗ |
| Регистр накопления | `РН_` | РН_ОстаткиНаСкладах, РН_ОстаткиНаСкладах_ММ |
| Регистр расчета | `РР_` | РР_Зарплата, РР_Зарплата_НЗ |
| Регистр сведений | `РС_` | РС_АдресныйКлассификатор |
| Бизнес процесс | `БП_` | БП_Согласование |
| Справочник | `Спр_` | Спр_Пользователи, Спр_Пользователи_МО |
| План счетов | `ПС_` | ПС_Хозрасчетный |
| План видов расчета | `ПВР_` | ПВР_Зарплатный |
| План видов характеристик | `ПВХ_` | ПВХ_Субконто, ПВХ_Субконто_ММ |
| Документ | `Док_` | Док_ПКО |
| Перечисление | `Пер_` | Пер_СтатусСогласования |
| План обмена | `ПО_` | ПО_РИБ, ПО_РИБ_ММ |
| Задача | `Зад_` | Зад_Задача |
| Обработка | `Обр_` | Обр_ЗакрытиеМесяца, Обр_ЗакрытиеМесяца_МО |
| Отчет | `Отч_` | Отч_УниверсальныйОтчет |
#### Суффиксы типов модулей
| Тип тестируемого модуля | Суффикс | Пример |
| ----------------------- | -------------- | ------------------------------------------ |
| Общий модуль | `<Без суффикса>` | ОМ_ОбщегоНазначения |
| Модуль объекта | `_МО` | Спр_Пользователи_МО, Обр_ЗакрытиеМесяца_МО |
| Модуль менеджера | `_ММ` | ПВХ_Субконто_ММ, ПО_РИБ_ММ |
| Модуль набора записей | `_НЗ` | РБ_Хозрасчетный_НЗ, РР_Зарплата_НЗ |
## Тестовые методы
Тест состоит из двух обязательных частей:
* Регистрация в методе `ИсполняемыеСценарии`
* Реализация теста.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

View File

@ -1,38 +0,0 @@
# Запуск
Для запуска тестов необходимо запустить 1С:Предприятие с параметром `RunUnitTests`, при необходимости можно указать путь к файлу [конфигурации запуска](configuration.md).
:::caution Важно
*После загрузки расширения в информационную базу необходимо отключить у него `безопасный режим` и `защиту от опасных действий`*
:::
## Запуск из EDT
Для запуска тестов из EDT необходимо установить [плагин](../install/install-plugin.md), либо вручную создать файл [конфигурации запуска](configuration.md) и указать его в параметрах запуска.
![EDT](images/from-edt.png)
## Запуск из конфигуратора
Для запуска тестов из конфигуратора вы можете
* Вручную создать файл [конфигурации запуска](configuration.md) и указать его в параметрах запуска
![Конфигуратор](images/from-configurator.png)
* Воспользоваться [интерфейсом настройки](../yaxunit-ui.md#интерфейс-настройки-конфигурации)
## Запуск из предприятия
Для запуска тестов из предприятия необходимо воспользоваться командой [Запуск тестирования](../yaxunit-ui.md#запуск-тестов)
![Запуск тестов](../images/ui-run-tests.png)
## Строка запуска предприятия
`[путь к клиенту 1С] ENTERPRISE [Параметры подключения к ИБ] [Параметры авторизации] /C RunUnitTests=/путь/к/конфигурационному/файлу`
Для формирования строки запуска можно воспользоваться [интерфейсом настройки](../yaxunit-ui.md#интерфейс-настройки-конфигурации)
Пример:
`"C:\Program Files\1cv8\8.3.18.1698\bin\1cv8c.exe" ENTERPRISE /IBName MyInfoBase /N Admin /C RunUnitTests=C:\tmp\test-config.json`
Запуск тестов в режиме тонкого клиента на информационной базе `MyInfoBase` под пользователем `Admin` по конфигурации указанной в файле `C:\tmp\test-config.json`

View File

@ -1,233 +0,0 @@
---
tags: [Начало, Утверждения]
---
# Базовые утверждения
Утверждения для проверки значений
Доступ к утверждениям обеспечивает метод `ЮТест.ОжидаетЧто`, который возвращает инициализированный модуль `ЮТУтверждения`, реализующий работу с утверждениями.
:::tip
Не рекомендуется обращаться к модулю `ЮТУтверждения` напрямую, используйте `ЮТест.ОжидаетЧто`
:::
* Реализован минимально необходимый набор проверок
* Большая часть методов - это сравнения фактического и ожидаемого результатов, но есть несколько методов настройки
* `Что` - устанавливает проверяемый объект. Все дальнейшие проверки будут выполняется с этим объектом
* `Метод` - устанавливает имя и параметры проверяемого метода. Для проверки методов имеются утверждения `ВыбрасываетИсключение` и `НеВыбрасываетИсключение`
* `Параметр` - добавляет параметр метода. Создан для удобства установки параметров проверяемого метода
* `Свойство` - устанавливает проверяемое свойство и проверяет его наличие.
* Последующие проверки, вызванные после этого метода, будут относиться к свойству объекта.
Например, `ЮТест.ОжидаетЧто(Контекст).Свойство("ИмяМетода").Равно("МетодБезИсключение")` эквивалентно проверке `Контекст.ИмяМетода = "МетодБезИсключение"`
* Методы работы со свойствами позволяют указывать цепочку свойств (доступ к вложенным свойствам через точку)
Например:
* `Свойство("Контекст.ИмяМетода")` - вложенное свойство
* `ИмеетСвойство("Контекст.ПараметрыМетода[0]")` - элемент вложенной коллекции
* `НеИмеетСвойства("[0].Свойство")` - свойство элемента коллекции
* Все методы имеют параметр `ОписаниеПроверки` для описания конкретной проверки
## Доступные методы
:::tip
Полный и актуальный набор методов смотрите в описании API
:::
### Сравнение значений
* `Равно` - проверка на равенство конкретному значению. Для сериализуемых объектов идет сравнение по значению
* `НеРавно` - проверка на не равенство конкретному значению. Для сериализуемых объектов идет сравнение по значению
* `Больше` - проверяемое значение должно быть больше указанного
* `БольшеИлиРавно` - проверяемое значение должно быть больше или равно указанному
* `Меньше` - проверяемое значение должно быть меньше указанного
* `МеньшеИлиРавно` - проверяемое значение должно быть меньше или равно указанному
* `ЭтоНеопределено` - проверяемое значение должно быть равно `Неопределено`
* `ЭтоНеНеопределено` - проверяемое значение должно быть не равно `Неопределено`
* `ЭтоNull` - проверяемое значение должно быть равно `Null`
* `ЭтоНеNull` - проверяемое значение должно быть не равно `Null`
* `ЭтоИстина` - проверяемое значение должно быть истиной
* `ЭтоНеИстина` - проверяемое значение не должно быть истиной
* `ЭтоЛожь` - проверяемое значение должно быть ложью
* `ЭтоНеЛожь` - проверяемое значение не должно быть ложью
### Проверка заполненности
* `Заполнено` - проверяет заполненность значения
* `НеЗаполнено` - проверяет незаполненность значения
* `Существует` - проверяет существование (не равно `Null` и `Неопределено`) значения
* `НеСуществует` - проверяет не существование (не равно `Null` и `Неопределено`) значения
### Проверка строк
* `ИмеетДлину` - проверяет, что строка имеет указанную длину
* `ИмеетДлинуБольше` - проверяет, что длин строки больше указанной
* `ИмеетДлинуМеньше` - проверяет, что длина строки меньше указанной
* `НеИмеетДлину` - проверяет, что длина строки отличается от указанной
* `Содержит` - проверяемая строка содержит указанную подстроку
* `НеСодержит` - проверяемая строка не содержит указанную подстроку
* `НачинаетсяС` - проверяемая строка начинается с указанной строки
* `ЗаканчиваетсяНа` - проверяемая строка заканчивается на указанную строку
* `СодержитСтрокуПоШаблону` - проверяемая строка содержит подстроку, соответствующую регулярному выражению
* `НеСодержитСтрокуПоШаблону` - проверяемая строка не содержит подстроку, соответствующую регулярному выражению
### Проверка вхождения значения в интервал
* `МеждуВключаяГраницы` - проверяемое значение находиться в указанному интервале (включая границы)
* `МеждуИсключаяГраницы` - проверяемое значение находиться в указанному интервале (исключая границы)
* `МеждуВключаяНачалоГраницы` - проверяемое значение находиться в указанному интервале (включая левую границу и исключая правую)
* `МеждуВключаяОкончаниеГраницы` - проверяемое значение находиться в указанному интервале (исключая левую границу и включая правую)
### Проверка типа значения
* `ИмеетТип` - проверяемое значение должно иметь указанный тип
* `НеИмеетТип` - тип проверяемого значения должен отличаться от указанного
### Проверка выполнения метода
* `ВыбрасываетИсключение` - проверят, что указанный метод объекта выбрасывает исключение
* `НеВыбрасываетИсключение` - проверят, что указанный метод объекта не выбрасывает исключение
### Проверка наличия свойств/реквизитов
* `ИмеетСвойство` - проверяемый объект должен содержать указанное свойство
* `НеИмеетСвойства` - проверяемый объект не содержит указанное свойство
* `ИмеетСвойстваРавные` - проверяемый объект должен содержать указанный набор свойств/реквизитов и значений
### Проверка коллекции
* `ИмеетДлину` - проверяет, что коллекция имеет указанный размер
* `ИмеетДлинуБольше` - проверяет, что коллекция имеет размер, который больше указанного
* `ИмеетДлинуМеньше` - проверяет, что коллекция имеет размер, который меньше указанного
* `НеИмеетДлину` - проверяет, что размер коллекции отличается от указанного
* `Содержит` - проверяемая коллекция должна содержать указанный элемент
* `НеСодержит` - проверяемая коллекция не должна содержать указанный элемент
* `КаждыйЭлементСодержитСвойство` - проверяет, что каждый элемент коллекции имеет указанное свойство
* `КаждыйЭлементСодержитСвойствоСоЗначением` - проверяет, что каждый элемент коллекции имеет указанное свойство, которое равно ожидаемому значению
* `ЛюбойЭлементСодержитСвойство` - проверяет, что в коллекции есть элемент содержащий указанное свойство
* `ЛюбойЭлементСодержитСвойствоСоЗначением` - проверяет, что в коллекции есть элемент содержащий указанное свойство, которое равно ожидаемому значению
* `КаждыйЭлементСоответствуетПредикату` - проверяет, что элементы коллекции соответствуют переданным условиям
* `ЛюбойЭлементСоответствуетПредикату` - проверяет, что коллекция содержит элемент, который соответствует переданным условиям
### Проверка на соответствие набору условий, предикату
* `СоответствуетПредикату` - проверяет, что объект или его свойство соответствует набору условий
* `КаждыйЭлементСоответствуетПредикату` - проверяет, что элементы коллекции соответствуют переданным условиям
* `ЛюбойЭлементСоответствуетПредикату` - проверяет, что коллекция содержит элемент, который соответствует переданным условиям
* `Содержит` - проверяемая коллекция должна содержать элемент, который соответствует переданным условиям
* `НеСодержит` - проверяемая коллекция не должна содержать элемент, который соответствует переданным условиям
### Проверка методов объекта
Для проверки работы методов объекта есть набор утверждений среди описанных выше (`ВыбрасываетИсключение` и `НеВыбрасываетИсключение`), но для их работы необходимо выполнить предварительные настройки.
Нужно указать какой методы объекта мы хотим проверить и с какими параметрами, для этого имеются следующие методы api
* `Метод` - устанавливает имя и параметры проверяемого метода
* `Параметр` - добавляет параметр метода. Создан для удобства установки параметров проверяемого метода
### Методы позиционирования
В дополнении к указанным методам утверждений есть возможность применить их к вложенным свойствам. Например, проверить, наличие заполненной табличной части документа используя выражение `ОжидаетЧто(Документ).Свойство("Товары").Заполнено()`.
Используя методы `Свойство` и `Элемент` можно позиционировать утверждения на вложенный реквизит/элемент.
* `Свойство` - проверяет наличие свойства и позиционирует дальнейшие проверки на указанном свойстве
* `Элемент` - проверяет наличие элемента коллекции и позиционирует дальнейшие проверки на указанном элементе
* `Объект` - позиционирует дальнейшие проверки на объекте, указанном в методе `Что`
* `НетСвойства` - проверяет отсутствие свойства и позиционирует дальнейшие проверки на объекте, указанном в методе `Что`
## Примеры
### Базовые проверки
```bsl
ЮТест.ОжидаетЧто(2 + 3, "2 + 3") // Используя модуль утверждений установим проверяемое значение и пояснение
.ИмеетТип("Число") // Проверим тип
.Заполнено() // Заполненность проверяемого значения
.Больше(0) // Сравним с нулем
.Равно(5); // Проверим ожидаемый результат
```
### Проверка сложного объекта
```bsl
Объект = ЮТКоллекции.ЗначениеВМассиве("1", "2", "3");
ЮТУтверждения.Что(Объект, "Проверка элементов массива")
.Содержит("1")
.НеСодержит(1)
.Элемент(0).Равно("1")
.Элемент(1).Равно("2")
.Элемент(-1).Равно("3")
.Свойство("[00]").Равно("1")
.Свойство("[1]").Равно("2")
.Свойство("[-1]").Равно("3")
.НетСвойства(3)
.НеИмеетСвойства("[3]");
Объект.Добавить(Новый Структура("Первый, Второй", 1, ЮТКоллекции.ЗначениеВМассиве(2)));
ЮТУтверждения.Что(Объект, "Проверка свойства элемента массива")
.Свойство("[3].Первый").Равно(1)
.Свойство("[3].Второй[-1]").Равно(2)
.Свойство("[3].Второй[0]").Равно(2)
```
### Проверка вызова метода
```bsl
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодБезИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение"))
.НеВыбрасываетИсключение()
.НеВыбрасываетИсключение("Ожидаемое исключение");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение", 2))
.ВыбрасываетИсключение("Слишком много фактических параметров");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодИсключение", ЮТКоллекции.ЗначениеВМассиве("Исключение"))
.ВыбрасываетИсключение("Исключение");
ЮТУтверждения.Что(ОМ_ЮТУтверждения)
.Метод("МетодБезИсключение")
.ВыбрасываетИсключение("Недостаточно фактических параметров");
```
### Проверка соответствия предикату
```bsl
Дата = ЮТест.Данные().СлучайнаяДата();
Объект = Новый Структура;
Объект.Вставить("Число", 1);
Объект.Вставить("Строка", "1");
Объект.Вставить("Дата", Дата);
Объект.Вставить("Массив", ЮТКоллекции.ЗначениеВМассиве(1, "1"));
ПроверкаЧисла = ЮТест.Предикат().Реквизит("Число")
.ИмеетТип(Тип("Число"))
.БольшеИлиРавно(1)
.МеньшеИлиРавно(10)
.Получить();
ПроверкаДаты = ЮТест.Предикат().Реквизит("Дата")
.ИмеетТип(Новый ОписаниеТипов("Дата"))
.Равно(Дата)
.Получить();
ЮТест.ОжидаетЧто(Объект)
.СоответствуетПредикату(ЮТест.Предикат()
.Заполнено()
.ИмеетТип("Структура"))
.СоответствуетПредикату(ПроверкаЧисла)
.СоответствуетПредикату(ПроверкаДаты)
;
```
### Проверка элементов коллекции на соответствие предикату
```bsl
ТаблицаРезультатов = ЮТест.Данные().ЗагрузитьИзМакета("ОбщийМакет.ЮТ_МакетТестовыхДанных.R2C1:R5C11", ОписанияТипов);
Ютест.ОжидаетЧто(ТаблицаРезультатов)
.ИмеетТип("Массив")
.ИмеетДлину(3)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.Реквизит("Товар").Заполнено().ИмеетТип("СправочникСсылка.Товары")
.Реквизит("Период").Заполнено().ИмеетТип("Дата")
.Реквизит("Количество").Заполнено().ИмеетТип("Число")
.Реквизит("Цена").Заполнено().ИмеетТип("Число")
)
```

View File

@ -1,44 +0,0 @@
---
tags: [Начало, Утверждения, ДанныеИБ]
---
# Утверждения для проверки данных информационной базы
Большая часть тестируемых методов так или иначе оставляет свой след в базе, создает или изменяет записи в ИБ.
Для проверки правильности работы метода нам необходимо проверить, что изменилось в базе. В этом могут помочь утверждения проверяющие записи ИБ.
Доступ к утверждениям обеспечивает метод `ЮТест.ОжидаетЧтоТаблицаБазы`, который возвращает инициализированный модуль `ЮТУтвержденияИБ`, реализующий работу с утверждениями для ИБ.
Утверждения позволяют проверить наличие и отсутствие записей по различным условиям, для задания условий используются [предикаты](../predicates.md)
```bsl
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.НеСодержитЗаписи(); // В базе нет товаров
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.СодержитЗаписи(); // В базе есть товары
ЮТест.ОжидаетЧтоТаблицаБазы("Справочник.Товары")
.СодержитЗаписи(ЮТест.Предикат() // В базе есть товары определенного поставщика
.Реквизит("Поставщик").Равно(ДанныеСправочника.Поставщик));
ЮТест.ОжидаетЧтоТаблицаБазы("РегистрСведений.КурсыВалют")
.НеСодержитЗаписи(ЮТест.Предикат() // Курс валюты не установлен
.Реквизит("Валюта").Равно(ДанныеРегистра.Валюта)
.Реквизит("Период").БольшеИлиРавно(ДанныеРегистра.Период));
```
## Доступные методы
* `СодержитЗаписи` - проверяет наличие записей по условиям
* `НеСодержитЗаписи` - проверяет отсутствие записей по условиям
* `СодержитЗаписиСНаименованием` - проверяет наличие в таблице записей с указанным наименованием
* `СодержитЗаписиСКодом` - проверяет наличие в таблице записей с указанным кодом
* `СодержитЗаписиСНомером` - проверяет наличие в таблице записей с указанным номером
* `НеСодержитЗаписиСНаименованием` - проверяет отсутствие в таблице записей с указанным наименованием
* `НеСодержитЗаписиСКодом` - проверяет отсутствие в таблице записей с указанным кодом
* `НеСодержитЗаписиСНомером` - проверяет отсутствие в таблице записей с указанным номером
:::tip
[Предлагайте](https://github.com/bia-technologies/yaxunit/issues) и [добавляйте](https://github.com/bia-technologies/yaxunit/pulls) свои утверждения
:::

View File

@ -1,26 +0,0 @@
---
tags: [Начало, Утверждения]
---
# Утверждения
Каждый тестовый сценарий должен проверить результат работы тестируемого метода, будь то функция или процедура.
Для написания таких проверок реализован механизм утверждений, позволяющий в лаконичной форме описать свои ожидания, требования к результату.
Как и большая часть API, утверждения реализованы по модели [текучих выражений](https://ru.wikipedia.org/wiki/Fluent_interface), с целью повышения удобства и читаемости тестов
```bsl
ЮТест.ОжидаетЧто(Контекст)
.ИмеетТип("Структура")
.Свойство("ПрефиксОшибки").Равно("Контекст метода с параметрами")
.Свойство("ИмяМетода").Заполнено().Равно("МетодБезИсключение")
.Свойство("ПараметрыМетода").ИмеетТип("Массив").ИмеетДлину(1);
```
YaxUnit содержит утверждения для:
* Проверки [значений](assertions-base.md#доступные-методы)
* Проверки [методов объекта на выброс исключений](assertions-base.md#проверка-методов-объекта)
* Проверки [данных информационной базы](assertions-db.md)

View File

@ -1,52 +0,0 @@
---
tags: [Начало, Контекст]
---
# Контекст
## Механизм контекстов
Одним из важнейших блоков работы тестового движка является механизм контекстов.
Он позволяет:
1. Хранить промежуточные данные и обеспечивать работу механизма текучих выражений.
2. Передавать между тестами необходимые данные.
3. Удалять тестовые данные.
И имеет несколько ограничений:
1. Не синхронизируется между клиентом и сервером.
2. Тестовые контексты имеют ограниченное время жизни. Например, контекст теста "живет" только в рамках теста и событиях "ПередКаждымТестом", "ПослеКаждогоТеста".
Для различных механизмов движка существуют различные контексты, такие как контекст утверждений, контекст теста и тд.
## Контексты тестового модуля
Разработчику тестов будут интересны следующие контексты:
* Контекст теста (`ЮТест.КонтекстТеста`) - живет в рамках одного теста. Доступен в каждом тесте и в обработчиках событий
* `ПередКаждымТестом`
* `ПослеКаждогоТеста`
* Контекст тестового набора (`ЮТест.КонтекстТестовогоНабора`) - живет в рамках набора тестов. Доступен в каждом тесте набора и в обработчиках событий
* `ПередТестовымНабором`
* `ПослеТестовогоНабора`
* `ПередКаждымТестом`
* `ПослеКаждогоТеста`
* Контекст тестового модуля (`ЮТест.КонтекстМодуля`) - живет в рамках тестового модуля. Доступен в каждом тесте модуля и в обработчиках событий
* `ПередВсемиТестами`
* `ПослеВсехТестов`
* `ПередТестовымНабором`
* `ПослеТестовогоНабора`
* `ПередКаждымТестом`
* `ПослеКаждогоТеста`
На каждом уровне исполнения есть возможность переопределить обработчики событий соответствующего контекста исполнения при помощи методов `Перед()` и `После()`. Настроенный обработчик события будет вызван _вместо_ основного. Пример:
```bsl
ЮТТесты
.ДобавитьТестовыйНабор("Набор1") // Будет вызван основной обработчик ПередТестовымНабором()
.ДобавитьТест("Тест1")
.ДобавитьТестовыйНабор("Набор2").Перед("Перед_Набор2") // Будет вызван обработчик Перед_Набор2()
.ДобавитьТест("Тест2");
```

View File

@ -1,25 +0,0 @@
---
tags: [Начало]
---
# Пользовательский API
Для разработчиков тестов расширение предоставляет API:
* [Регистрации тестовых сценариев](test-registration.md)
* Формирования [утверждений](assertions/assertions.md) для проверки результата работы различных механизмов системы.
* Создания [тестовых данных](test-data/test-data.md)
* Сохранения состояния и обмена данными между тестами используя [контекст](context.md)
* [Подмены](mockito.md) алгоритмов работы системы
* [Предикаты](predicates.md), которые используются во многих механизмах движка
* Методы [получения данных](queries.md) информационной базы
* [Интерфейс](yaxunit-ui.md) для просмотра отчета и настройки параметров запуска тестов
Доступ к API осуществляется через методы модуля `ЮТест`, например
* `ЮТест.Данные()` для доступа к методам работы с тестовыми данными
* `ЮТест.ОжидаетЧто()` для доступа к утверждения
:::tip
Не рекомендуется обращаться к модулям API напрямую, используйте `ЮТест`
:::

View File

@ -1,285 +0,0 @@
---
tags: [Начало, Мокирование]
---
# Мокито
Мокито - модуль созданный по образу популярного java-фреймворка для тестирования [Mockito](https://site.mockito.org/). Расширяет возможности тестирования, позволяет легко менять логику работы системы подменяя результаты работы методов, отключая какие-либо алгоритмы и проверки.
Юнит-тесты, это тесты конкретных методов в отрыве от системы - контролировать данные используемые методом, изменение логики и ошибки других объектов не должны аффектить на тест.
В реальных конфигурациях объекты тесно связаны друг с другом, поэтому добиться контроля влияющих данных очень сложно. Обычно приходится создавать большой объем тестовых данных. А добиться изоляции от изменения логики других объектов почти невозможно. Мокирование же позволяет изменить логику работы системы таким образом, чтобы тестируемый метод не вызывал другие методы и использовал уже подготовленные данные. Например, для тестирования проведения реализации товаров мы можем подменить результат функции формирующей таблицу проводок и избежать сложной подготовки данных.
С помощью Мокито разработчик указывает, что делать при вызове определенных методов - вернуть нужный результат, вызвать исключение или просто не трогать ненужные методы. После тестирования разработчик может запросить и проверить статистику о вызовах, как и какие методы были вызваны.
Пример:
```bsl
ОтветСерверы = ОтветУспешногоЗапроса("Серверы");
ОтветДиски = ОтветУспешногоЗапроса("Диски");
Мокито.Обучение(РаботаСHTTP)
.Когда(РаботаСHTTP.ВыполнитьЗапрос(ПараметрыПодключения, "/hosts", "GET"))
.Вернуть(ОтветСерверы)
.Когда(РаботаСHTTP.ВыполнитьЗапрос(ПараметрыПодключения, "/disks", "GET"))
.Вернуть(ОтветДиски)
.Прогон();
Результат = БиллингДрайверГипервизорNutanix.Серверы(ПараметрыПодключения);
```
В этом примере изменяется работа модуля `РаботаСHTTP`, для функции `ВыполнитьЗапрос`, вызванной с нужными параметрами будет возвращено подготовленное значение, а сам метод не будет вызван.
Мы получим ожидаемые ответы на запросы к сторонней системе и уйдет от проблем связанных с ней - недоступность, изменение контрактов и т.д.
Работа с Мокито делится на 3 стадии:
```mermaid
flowchart LR
training(Обучение) --> run(Прогон, запуск тестового метода)
run --> check(Проверка)
```
* [Обучение](#обучение) - настраиваем поведение методов системы
* [Прогон](#прогон) - выполнение теста целевого метода
* [Проверка](#проверка) - анализ вызовов
## Использование
### Настройка мокируемых методов
Для работы Мокито вам необходимо добавить интересующие методы в тестовое расширение.
Эта позволит управлять поведением метода:
* подменять результат во время выполнения теста
* использовать явный вызов метода с параметрами на стадии обучения, например `Мокито.Обучение(Справочники.ИсточникиДанных).Когда(Справочники.ИсточникиДанных.СохраненныеБезопасныеДанные(Справочник)).Вернуть(Результат)`
* использовать явный вызов метода с параметрами на стадии проверки, например `Мокито.Проверить(Справочники.ИсточникиДанных).КоличествоВызовов(Справочники.ИсточникиДанных.СохраненныеБезопасныеДанные(Справочник)).Больше(1)`
Примеры добавления методов в расширение.
#### Метод общего модуля
Добавляем обработку метода `ИнициализироватьВнешнююКомпоненту` общего модуля `ОбщегоНазначенияКлиентСервер`
```bsl
&Вместо("ИнициализироватьВнешнююКомпоненту")
Функция ЮТИнициализироватьВнешнююКомпоненту(ИмяМакета, ИмяКомпоненты, ВызыватьИсключение, ПаузаЧерезКомпоненту) Экспорт
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(ИмяМакета, ИмяКомпоненты, ВызыватьИсключение, ПаузаЧерезКомпоненту);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ОбщегоНазначенияКлиентСервер, "ИнициализироватьВнешнююКомпоненту", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(ИмяМакета, ИмяКомпоненты, ВызыватьИсключение, ПаузаЧерезКомпоненту);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции
```
#### Метод модуля менеджера
Добавляем обработку метода `СохраненныеБезопасныеДанные` модуля менеджера справочника `Справочники.ИсточникиДанных`
```bsl
&Вместо("СохраненныеБезопасныеДанные")
Функция ЮТСохраненныеБезопасныеДанные(Владелец, Знач Ключи) Экспорт
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(Владелец, Ключи);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(Справочники.ИсточникиДанных, "СохраненныеБезопасныеДанные", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(Владелец, Ключи);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции
```
#### Метод модуля объекта
Добавляем обработку приватного метода `ПеренестиДанныеВБезопасноеХранилище` модуля объекта справочника `Справочники.ИсточникиДанных`
```bsl
&Вместо("ПеренестиДанныеВБезопасноеХранилище")
Функция ЮТПеренестиДанныеВБезопасноеХранилище(Ключи)
// Собираем параметры в массив
ПараметрыМетода = Мокито.МассивПараметров(Ключи);
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ЭтотОбъект, "ПеренестиДанныеВБезопасноеХранилище", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(Ключи);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции
```
### Обучение
Самая первая стадия при написании тестов использующих моки - обучение.
Мы создаем правила как будет вести себя метод при различных вариантах вызова.
Правило состоит из условий проверки параметров и действия выполняемого при соблюдении условий.
Условия можно задать на равенство определенному значению, на проверку типа переданного значения или же безусловно принимать любый параметры
Существует 2 основных подхода к формированию условий вызова:
1. Явный вызов метода с параметрами: `Обучение(РаботаСHTTP).Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Данные)).Вернуть(2)`
2. Указание имени метода и набора параметров: `Обучение(РаботаСHTTP).Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(ИсточникДанных, Данные)).Вернуть(2)`
Первый вариант имеет ряд недостатков:
1. Работает только для экспортных методов
2. Необходимо передавать все обязательные параметры или использовать для них маску `Мокито.ЛюбойПараметр()`
3. Если не указывать необязательные параметры, то их значения по умолчанию попадут в настройку. Покажу на примере.
Имеется метод `Функция Метод(Параметр1, Параметр2, Параметр3 = 3)`
Настройка `Когда(Метод(1, 2)).Вернуть(0)`, в результате ноль мы получим для вызовов
* `Метод(1, 2)`
* `Метод(1, 2, 3)`
Для вызова `Метод(1, 2, 4)` будет выполнен основной алгоритм метода.
А для настройки `Когда("Метод", Мокито.МассивПараметров(1, 2)).Вернуть(0)` все три варианта вызова вернут ноль.
После того как определились с условием вызова указанным в методе `Когда` нужно указать реакцию. Возможные реакции:
* `Вернуть` - вернуть указанное значение
* `ВыброситьИсключение` - вызвать исключение с переданным текстом
* `Пропустить` - пропустить выполнение метод (актуально для процедур)
#### Примеры формирования различных вариантов условий
Имеется метод:
```bsl
Функция ОтправитьОбъектНаСервер(ИсточникДанных, Объект, HTTPМетод = "POST",
ТипКонтента = "json", Преобразование = Неопределено,
ДопНастройки = Неопределено, Ответ = Неопределено, ОтветВСтруктуру = Ложь,
ТелоОтветаВХранилище = Ложь) Экспорт
```
* Переопределить все вызовы метода - указываем имя метода без указания параметров
`Мокито.Обучение(РаботаСHTTP).Когда("ОтправитьОбъектНаСервер").Вернуть(1)`
* Переопределить вызов, когда первый параметр имеет определенное значение
1. `Мокито.Обучение(РаботаСHTTP).Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(ИсточникДанных)).Вернуть(2)`
2. `Мокито.Обучение(РаботаСHTTP).Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Мокито.ЛюбойПараметр()).Вернуть(2)`. Тут используется маска `Мокито.ЛюбойПараметр()`, тк второй параметр является обязательным
* Переопределить вызов, когда **второй** параметр имеет определенное значение
1. `Мокито.Обучение(РаботаСHTTP).Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ЛюбойПараметр(), Объект)).Вернуть(2)`
2. `Мокито.Обучение(РаботаСHTTP).Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ЛюбойПараметр(), Объект).Вернуть(2)`.
* Условие на тип параметра
1. `Мокито.Обучение(РаботаСHTTP).Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ТипизированныйПараметр(ТипИсточникДанных), Мокито.ЧисловойПараметр())).Вернуть(3)`
2. `Мокито.Обучение(РаботаСHTTP).Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ТипизированныйПараметр(ТипИсточникДанных), Мокито.ЧисловойПараметр()).Вернуть(3)`
### Прогон
После обучения, настройки реакций на вызовы методов, можно запускать тест нужного метода. Для перехода к этому режиму работы Мокито используется метод `Прогон`.
Все вызовы к настроенным методам (добавленным в расширение) будут перехватываться и анализироваться на совпадение условий вызова.
Для вызовов, у которых есть подходящая "реакция" будет переопределено выполнение и запустится соответствующая реакция (вернуть значение, вызвать исключение и тд), для прочих - выполнение продолжится.
```bsl
// Настройка
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Мокито.ЛюбойПараметр(), Мокито.ЛюбойПараметр()))
.Вернуть(РезультатПоУмолчанию)
.Когда("ОтправитьОбъектНаСервер", Мокито.МассивПараметров(Справочники.ИсточникиДанных.FTP, 2))
.Вернуть(2)
.Прогон(); // Перевод в режим прогона теста
Результат = РаботаСHTTP.ОтправитьОбъектНаСервер(ИсточникДанных, Данные); // Результат будет равен переменной РезультатПоУмолчанию
Результат = РаботаСHTTP.ОтправитьОбъектНаСервер(Справочники.ИсточникиДанных.FTP, 2); // Результат будет равен 2
```
### Проверка
После прогона теста можно проверить какие методы, с какими параметрами вызывались. Для этих целей необходимо воспользоваться методом `Проверить`
```bsl
Мокито.Проверить(РаботаСHTTP) // Устанавливаем проверяемый объект
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(ЛюбойПараметр, Мокито.ЧисловойПараметр())) // Условия поиска вызовов
.Больше(1) // Проверки
.Равно(2)
.КоличествоВызовов("ОтправитьОбъектНаСервер").Заполнено().Равно(3).Меньше(6)
.КоличествоВызовов("ОтправитьЗапросHTTP").Пусто().Меньше(1)
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(1, 2)).Равно(1)
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(ЛюбойПараметр, ЛюбойПараметр)).Равно(3)
.КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(Мокито.ТипизированныйПараметр(ТипИсточникДанных), ЛюбойПараметр)).Равно(1)
```
Принцип формирования проверки:
* Указываем проверяемый объект `Проверить(РаботаСHTTP)`.
* Указываем условия поиска вызовов метода. Логика формирования условия такая же как при обучении.
Например, `КоличествоВызовов(РаботаСHTTP.ОтправитьОбъектНаСервер(ЛюбойПараметр, Мокито.ЧисловойПараметр()))`
Соберет все вызовы метода `РаботаСHTTP.ОтправитьОбъектНаСервер`, к которых вторым параметром идет число, а 3й и последующий параметры имеют значения по умолчанию.
* Проверяем собранные вызовы:
* `Заполнено` - есть вызовы метода по указанным условиям
* `Пусто` - нет вызовов метода по указанным условиям
* `Равно` - количество вызовов попавших под условия равно указанному значению
* `Больше` - количество вызовов попавших под условия больше указанного значения
* `Меньше` - количество вызовов попавших под условия меньше указанного значения
### Ограничения параметров
При обучении (а также при проверке) не редко мы не может указать конкретные значения, которые придут в метод. для этих целей имеются.
* Маски параметров
* `ЛюбойПараметр` - ожидаем любой параметр
* `ЧисловойПараметр` - ожидаем только числовой параметр
* `СтроковыйПараметр` - ожидаем только строковый параметр
* `ТипизированныйПараметр` - ожидаем параметр указанного типа
* [Предикаты](predicates.md) позволяющие сформировать почти любые условия на параметры
Примеры
* `Когда(Метод(1, 2))`
* `Когда(Метод(Мокито.ЧисловойПараметр(), 2))`
* `Когда(Метод(ЮТест.Предикат().Больше(0), 2))`
## Кейсы использования\*
\* *В примерах опускается часть добавления метода в расширение*
1. Подмена результат функции для любого вызова
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер")
.Вернуть(1)
```
2. Выключение алгоритма проведения документа
```bsl
Мокито.Обучение(СсылкаИлиОбъектДокумент)
.Когда("ОбработкаПроведения")
.Пропустить()
```
3. Выбросить исключение, если в метод передан некорректный набор параметров
```bsl
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьОбъектНаСервер")
.ВыброситьИсключение("Не верные параметры вызова")
.Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Справочники.ИсточникиДанных.FTP, Мокито.ЛюбойПараметр()))
.ВыполнитьМетод();
```

View File

@ -1,145 +0,0 @@
---
tags: [Начало, Предикаты, Утверждения, Запросы, Мокирование]
---
# Предикаты
Предикаты это утверждения, которые вы можете передавать в качестве параметров.
Они расширяют и унифицируют функциональность тестового движка.
```bsl
Процедура АктуализацияУведомлений() Экспорт
ИмяРегистра = "РегистрСведений.ОповещенияПользователя";
Объект = ТестовыеДанные.Объект();
УсловиеУведомления = ЮТест.Предикат()
.Реквизит("Источник").Равно(Объект)
.Реквизит("ТипОповещения").Равно(Справочники.ТипыОповещенийПользователя.Уведомление1)
.Получить();
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.НеСодержитЗаписи(УсловиеУведомления);
УведомленияВызовСервера.АктуализацияУведомлений();
ЮТест.ОжидаетЧтоТаблицаБазы(ИмяРегистра)
.СодержитЗаписи(УсловиеУведомления);
ДанныеУведомления = ЮТЗапросы.Запись(ИмяРегистра, УсловиеУведомления);
ЮТест.ОжидаетЧто(ДанныеУведомления)
.Свойство("Прочитано").ЭтоЛожь()
.Свойство("Пользователь").Равно(Справочники.ГруппыОповещенийПользователей.Инженер);
КонецПроцедуры
```
Механизм предикатов:
* позволяет формировать наборы утверждений и передавать их в качества параметров;
* используется для проверки коллекций, записей базы и так далее;
* построен по модели текучих выражения и имеет схожий с базовыми утверждениями синтаксис (`ЮТест.ОжидаетЧто()`);
## Примеры использования
* Проверка коллекции
```bsl
ЮТест.ОжидаетЧто(Коллекция)
.ЛюбойЭлементСоответствуетПредикату(ЮТест.Предикат()
.Реквизит("Число").Равно(2)); // Проверят, что в коллекции есть элементы с реквизитом `Число`, которое равно `2`
ЮТест.ОжидаетЧто(Коллекция)
.Содержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2)); // Тоже самое, что и проверка выше
ЮТест.ОжидаетЧто(Коллекция)
.КаждыйЭлементСоответствуетПредикату(ЮТест.Предикат()
.Заполнено().ИмеетТип("Массив")); // Проверят, что каждый элемент коллекции это заполненный массив
ЮТест.ОжидаетЧто(Коллекция)
.НеСодержит(ЮТест.Предикат()
.Реквизит("Число").Равно(2)); // Проверят, что в коллекции нет элементов с реквизитом `Число`, которое равно `2`
```
* Описания параметров метода при мокировании
Например, имеем метод, который принимает в параметрах структуру. Необходимо вернуть 2 разных результата в зависимости от значения реквизита входной структуры.
```bsl
Мокито.Обучение(Модуль)
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Сложить")))
.ВернутьРезультат(Результат1)
.Когда(Модуль.Посчитать(ЮТест.Предикат()
.Реквизит("Оператор").Равно("Вычесть")))
.ВернутьРезультат(Результат2);
```
* Утверждения, проверяющие данные в базе на основании предикатов.
```bsl
ЮТест.ОжидаетЧтоТаблица("Справочник.Товары").СодержитЗаписи(
ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение)
);
```
* Получение записей из базы
```bsl
ДанныеТовара = ЮТЗапросы.Запись("Справочник.Товары", ЮТест.Предикат()
.Реквизит("Наименование").Равно("Товар 1")
.Реквизит("Ссылка").НеРавно(Исключение));
```
## Особенности
### Особенности контекста
Предикаты как и большинство механизмов построены на текучих выражениях с сохранением состояния в глобальном контексте.
Это приводит к тому, что вы не можете сразу использовать несколько предикатов, например
```bsl
Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка"),
ЮТест.Предикат().ИмеетТип("Число")))
.ВернутьРезультат(Результат1);
```
В этом примере 1С сначала вычислит выражения для всех параметров, а потом передаст их в метод и мы получим для обоих параметров один и тот же предикат, ожидающий тип `Число`.
Потому что состояние первого предиката будет заменено вторым. Для обхода этой проблемы можно использовать метод `Получить`, который возвращает текущее состояние.
```bsl
Мокито.Обучение(Модуль)
.Когда(Модуль.СделатьЧтоТо(
ЮТест.Предикат().ИмеетТип("Строка").Получить(),
ЮТест.Предикат().ИмеетТип("Число")))
.ВернутьРезультат(Результат1);
```
Такая же история при сохранение предикатов в переменные.
```bsl
ПроверкаСтрока = ЮТест.Предикат().ИмеетТип("Строка");
ПроверкаЧисло = ЮТест.Предикат().ИмеетТип("Число");
```
`ПроверкаСтрока` и `ПроверкаЧисло` будут равны и содержать одинаковые условия. Проблему также можно обойти используя метод `Получить`.
```bsl
ПроверкаСтрока = ЮТест.Предикат().ИмеетТип("Строка").Получить();
ПроверкаЧисло = ЮТест.Предикат().ИмеетТип("Число").Получить();
```
### Особенности реализации
Сам модуль предикатов используется только для формирования утверждений/условий.
Реализацией проверок и формированием условий занимаются другие модули и возможна ситуация, когда некоторые предикаты еще не реализованы или не поддерживаются каким-либо механизмом. Например, проверка заполненности не поддерживается запросами.

View File

@ -1,20 +0,0 @@
---
tags: [Начало, ДанныеИБ]
---
# Запросы
Для получения данных базы предоставлен API выполнения запросов `ЮТЗапросы`
Позволяет как с сервера, так и с клиента получать данные информационной базы и имеет следующие методы
* `ЗначенияРеквизитов` - Возвращает значения реквизитов ссылки
* `ЗначениеРеквизита` - Возвращает значение реквизита ссылки
* `Запись` - Возвращает первую запись таблицы, соответствующую условиям
* `Записи` - Возвращает записи таблицы, соответствующую условиям
* `ЗначенияРеквизитовЗаписи` - Возвращает значения реквизитов первой записи таблицы, соответствующей условиям
* `ЗначениеРеквизитаЗаписи` - Возвращает значение реквизита первой записи таблицы, соответствующей условиям
* `ТаблицаСодержитЗаписи` - Вернет признак, содержит ли таблица записи, удовлетворяющие переданным условиям
* `РезультатЗапроса` - Возвращает результат выполнения простого запроса, используя описание запроса
* `РезультатПустой` - Определяет, есть ли в результате записи, используя описание запроса
* `ОписаниеЗапроса` - Формирует описание простого запроса

View File

@ -1,99 +0,0 @@
---
tags: [Начало, Тестовые данные]
---
# Удаление тестовых данных
При использовании тестовых данных нередко необходимо удалять созданные объекты.
Для этого вы можете использовать
## Автоматические транзакции
Для включения нужно при регистрации теста вызвать метод `ВТранзакции()`
```bsl title=ВТранзакции.bsl
ЮТТесты
.ДобавитьТест("Фикция").ВТранзакции() // Использование транзакции для конкретного теста
ЮТТесты
.ДобавитьТестовыйНабор("Основной).ВТранзакции() // Использование транзакции для набора тестов
.ДобавитьТест("Фикция")
ЮТТесты.ВТранзакции() // Использование транзакции для тестов модуля
.ДобавитьТест("Фикция")
```
И тогда тестовый движок будет оборачивать в транзакцию каждый серверный тест (для клиентских будет игнорироваться)
:::caution Учитывайте
В транзакцию оборачивается тест, а не модуль или набор, поэтому данные созданные вне теста не будут удалены.
Такие данные необходимо удалять самостоятельно при необходимости.
:::
## Механизм удаления тестовых данных
Для включения нужно при регистрации теста вызвать метод `УдалениеТестовыхДанных()`
```bsl title=УдалениеТестовыхДанных.bsl
ЮТТесты
.ДобавитьТест("Фикция").УдалениеТестовыхДанных() // Использование для конкретного теста
ЮТТесты
.ДобавитьТестовыйНабор("Основной).УдалениеТестовыхДанных() // Использование для всех тестов набора
.ДобавитьТест("Фикция")
ЮТТесты.УдалениеТестовыхДанных() // Использование для всех тестов модуля
.ДобавитьТест("Фикция")
ЮТТесты.ВТранзакции().УдалениеТестовыхДанных() // Совместно с транзакцией
.ДобавитьТест("Фикция")
```
Механизм не использует транзакции, а запоминает в контексте все созданные с помощью API объекты и записи регистров сведений.
Позволяет:
* Удалять данные созданные с клиента
* Удалять данные созданные вне теста (в обработчиках событий)
:::caution Удаление и только
Механизм только удаляет созданные объекты, изменения не откатываются
:::
:::tip Разное время жизни данных
Механизм понимает в рамках какого контекста исполнения (тест, набор, модуль) были созданы данные и удаляет их по выходу из него.
:::
Рассмотрим время жизни объектов созданных на разных этапах тестирования, например имеем такой модуль.
```bsl title=УдалениеТестовыхДанныхВремяЖизни.bsl
Процедура ПередВсемиТестами() Экспорт
ДанныеМодуля = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура ПередТестовымНабором() Экспорт
ДанныеНабора = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура ПослеВсехТестов() Экспорт
ДанныеТеста = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
Процедура Тест() Экспорт
Ссылка = ЮТест.Данные().СоздатьЭлемент();
КонецПроцедуры
```
Для переменных получим разное время жизни
* `ДанныеМодуля` - Живет, пока исполняются тесты модуля одного контекста (Сервер, Клиент).
* `ДанныеНабора` - Живет, пока исполняются тесты набора.
* `ДанныеТеста` и `Ссылка` - Живут, пока исполняется тест.
:::caution Механизм имеет ряд ограничений и не работает для следующих кейсов:
* Данные создан не через API
* Данные созданы в клиентском модуле через вызов своего серверного модуля, даже если он использует API тестового движка
:::

View File

@ -1,39 +0,0 @@
---
tags: [Начало]
---
# Регистрация тестовых методов (ЮТТесты)
Кроме того, чтобы написать тестовые сценарии, разработчик должен зарегистрировать их в движке.
Регистрация выполняется внутри предопределенного экспортного метод `ИсполняемыеСценарии`, который обязательно должен находиться в тестовом модуле.
```bsl
Процедура ИсполняемыеСценарии() Экспорт
// Регистрация тестов
ЮТТесты // Регистрация тестов выполняет через модуль регистратор
.ДобавитьТестовыйНабор("Математические методы") // Набор - объединение тестов
.ДобавитьТест("Сложение") // Обязательно при регистрации указываем имя экспортного метода
.ДобавитьТест("Вычитание", "Вычитание") // Также можно указать представление теста
.ДобавитьТест("Вычитание", "ВычитаниеСервер", , "Сервер") // Контекст исполнения, по умолчанию тест выполняется во всех контекстах модуля
.ДобавитьКлиентскийТест("УмножениеНаКлиенте") // Есть отдельный метод для регистрации клиентских тестов
.ДобавитьСерверныйТест("ДелениеНаСервере", "Деление на сервер") // Есть отдельный метод для регистрации серверных тестов
.ДобавитьТестовыйНабор("Строковые методы")
.ДобавитьТест("СтрНайти")
.ДобавитьТест("СтрРазделить");
КонецПроцедуры
```
Этот метод автоматически вызывается тестовым движком при старте тестирования.
Как видно из примера выше, регистрация выполняется с помощью методов модуля `ЮТТесты`.
Он позволяет:
* Объединять тесты в наборы, указывать теги, по которым возможно формировать отборы запускаемых тестов
* Указывать контекст вызова, например, для клиент-серверного модуля (или метода) можно реализовать тесты в одном тестовом модуле, проверяющие логику и на клиенте, и на сервере.
* Указывать параметры выполнения тестов.
* Организовывать черновики, регистрируя нереализованные тесты.

View File

@ -1,3 +1,8 @@
---
sidebar_position: 5
tags: [Начало]
---
# Графический интерфейс
Расширение добавляет в командный интерфейс конфигурации новый раздел "Юнит тест".

View File

@ -1,8 +1,7 @@
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
const lightCodeTheme = require('prism-react-renderer/themes/github');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
import {themes as prismThemes} from 'prism-react-renderer';
/** @type {import('@docusaurus/types').Config} */
const config = {
@ -95,13 +94,13 @@ const config = {
navbar: {
title: 'YAx Unit',
logo: {
alt: 'YaxUnit Logo',
alt: 'YAxUnit Logo',
src: 'img/logo.png',
},
items: [
{
type: 'doc',
docId: 'user-api/index',
docId: 'features/features',
position: 'left',
label: 'Описание',
},
@ -142,7 +141,7 @@ const config = {
items: [
{
label: 'Tutorial',
to: '/docs/user-api',
to: '/docs/features',
},
],
},
@ -168,14 +167,25 @@ const config = {
copyright: `Copyright © ${new Date().getFullYear()} BIA Technologies, Inc. Built with Docusaurus.`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
theme: prismThemes.vsLight,
darkTheme: prismThemes.vsDark,
additionalLanguages: ['bsl'],
},
}),
markdown: {
mermaid: true,
},
themes: ['@docusaurus/theme-mermaid'],
themes: ['@docusaurus/theme-mermaid',
["@easyops-cn/docusaurus-search-local", {
hashed: true,
language: ["en", "ru"],
indexBlog: false,
indexDocs: true,
docsRouteBasePath: ["docs", "api", 'contributing', 'lessons'],
docsDir: ["docs", "api", 'contributing', 'lessons'],
highlightSearchTermsOnTargetPage: true,
hideSearchBarWithNoSearchContext: true,
}]],
};
module.exports = config;

View File

@ -14,17 +14,20 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@docusaurus/core": "2.3.1",
"@docusaurus/preset-classic": "2.3.1",
"@docusaurus/theme-mermaid": "2.3.1",
"@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
"react-dom": "^17.0.2"
"@docusaurus/core": "^3.1.1",
"@docusaurus/preset-classic": "^3.1.1",
"@docusaurus/theme-mermaid": "^3.1.1",
"@easyops-cn/docusaurus-search-local": "^0.40.1",
"@mdx-js/react": "^3.0.0",
"dagre": "^0.8.5",
"prism-react-renderer": "^2.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"reactflow": "^11.10.1"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "2.3.1"
"@docusaurus/module-type-aliases": "^3.1.1"
},
"browserslist": {
"production": [
@ -39,6 +42,6 @@
]
},
"engines": {
"node": ">=16.14"
"node": ">=18.0"
}
}

View File

@ -0,0 +1,121 @@
import React, { useCallback } from 'react';
import ReactFlow, {
addEdge,
ConnectionLineType,
Panel,
useNodesState,
useEdgesState,
} from 'reactflow';
import dagre from 'dagre';
import { useHistory } from "react-router-dom";
// we have to import the React Flow styles for it to work
import 'reactflow/dist/style.css';
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeWidth = 150;
const nodeHeight = 36;
const getLayoutedElements = (nodes, edges, direction = 'LR') => {
const isHorizontal = direction === 'LR';
dagreGraph.setGraph({ rankdir: direction });
nodes.forEach((node) => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach((edge) => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
nodes.forEach((node) => {
const nodeWithPosition = dagreGraph.node(node.id);
node.targetPosition = isHorizontal ? 'left' : 'top';
node.sourcePosition = isHorizontal ? 'right' : 'bottom';
// We are shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
node.position = {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2,
};
return node;
});
return { nodes, edges };
};
const Mindmap = (nodesTree) => {
const initialNodes = []
const initialEdges = []
var id = 0;
const fillNodes = (nodeTree, parentID)=>{
console.log(nodeTree);
const nodeId = 'n' + (id++);
const node = { id: nodeId, data: { label: nodeTree.label, href: nodeTree.href } };
initialNodes.push(node);
if(parentID){
initialEdges.push({ id: 'e' + parentID + nodeId, source: parentID, target: nodeId });
} else {
node.type = 'input'
}
if(nodeTree.child){
nodeTree.child.forEach(childNode=>{
fillNodes(childNode, nodeId);
})
} else{
node.type = 'output'
}
}
fillNodes(nodesTree.nodesTree, undefined);
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(
initialNodes,
initialEdges
);
const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
const onConnect = useCallback(
(params) =>
setEdges((eds) =>
addEdge({ ...params, type: ConnectionLineType.SmoothStep, animated: true }, eds)
),
[]
);
const history = useHistory();
const onNodeClick = (event, node) => history.push(node.data.href);
return (
<div style={{ width: '100%', height: '1000px' }}>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
connectionLineType={ConnectionLineType.SmoothStep}
//fitView
onNodeClick={onNodeClick}
// zoomOnScroll={false}
// panOnDrag={true}
nodesDraggable={false}
/>
</div>
);
};
const Node = (label, href, child) =>{
return {label: label, href: href, child: child};
}
export { Mindmap, Node };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 1000 1000" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 53.2 (72643) - https://sketchapp.com -->
<title>Artboard</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="99.2583404%" id="linearGradient-1">
<stop stop-color="#2AABEE" offset="0%"></stop>
<stop stop-color="#229ED9" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<circle id="Oval" fill="url(#linearGradient-1)" cx="500" cy="500" r="500"></circle>
<path d="M226.328419,494.722069 C372.088573,431.216685 469.284839,389.350049 517.917216,369.122161 C656.772535,311.36743 685.625481,301.334815 704.431427,301.003532 C708.567621,300.93067 717.815839,301.955743 723.806446,306.816707 C728.864797,310.92121 730.256552,316.46581 730.922551,320.357329 C731.588551,324.248848 732.417879,333.113828 731.758626,340.040666 C724.234007,419.102486 691.675104,610.964674 675.110982,699.515267 C668.10208,736.984342 654.301336,749.547532 640.940618,750.777006 C611.904684,753.448938 589.856115,731.588035 561.733393,713.153237 C517.726886,684.306416 492.866009,666.349181 450.150074,638.200013 C400.78442,605.66878 432.786119,587.789048 460.919462,558.568563 C468.282091,550.921423 596.21508,434.556479 598.691227,424.000355 C599.00091,422.680135 599.288312,417.758981 596.36474,415.160431 C593.441168,412.561881 589.126229,413.450484 586.012448,414.157198 C581.598758,415.158943 511.297793,461.625274 375.109553,553.556189 C355.154858,567.258623 337.080515,573.934908 320.886524,573.585046 C303.033948,573.199351 268.692754,563.490928 243.163606,555.192408 C211.851067,545.013936 186.964484,539.632504 189.131547,522.346309 C190.260287,513.342589 202.659244,504.134509 226.328419,494.722069 Z" id="Path-3" fill="#FFFFFF"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -12,174 +12,27 @@ slug: /
[![GitHub Releases](https://img.shields.io/github/downloads/bia-technologies/yaxunit/latest/total?style=flat-square)](https://github.com/bia-technologies/yaxunit/releases)
[![Quality Gate](https://sonar.openbsl.ru/api/project_badges/measure?project=yaxunit&metric=alert_status)](https://sonar.openbsl.ru/dashboard?id=yaxunit)
[![Maintainability](https://sonar.openbsl.ru/api/project_badges/measure?project=yaxunit&metric=sqale_rating)](https://sonar.openbsl.ru/dashboard?id=yaxunit)
[![Build and test](https://github.com/bia-technologies/yaxunit/actions/workflows/main-build.yml/badge.svg)](https://github.com/bia-technologies/yaxunit/actions/workflows/main-build.yml)
[![Build ocumentation](https://github.com/bia-technologies/yaxunit/actions/workflows/deploy-documentation.yml/badge.svg)](https://github.com/bia-technologies/yaxunit/actions/workflows/deploy-documentation.yml)
Обсудить в [Telegram чате](https://t.me/BIAOpenTools/12)
[![Telegram чате](images/telegram.svg)](https://t.me/BIAOpenTools/12)
----
- [Назначение](#назначение)
- [Возможности](#возможности)
- [Пример тестового модуля](#пример-тестового-модуля)
- [Запуск](#запуск)
- [Запуск из EDT](#запуск-из-edt)
- [Запуск вне EDT](#запуск-вне-edt)
- [Благодарности](#благодарности)
- [Лицензия](#лицензия)
**YAxUnit** представляет собой мощный инструмент написания тестов для решений на платформе 1С:Предприятие.
Он был создан с учетом лучших практик тестирования и предлагает [множество функций](/docs/features/), которые позволяют эффективно справиться с поставленной задачей.
## Назначение
* Тестовый движок, который выполнит тесты и предоставит подробный отчет в нужном формате.
* [Утверждения](/docs/features/assertions/) для проверки ожидаемых результатов. Они помогают разработчикам ясно и точно определить, какие результаты они ожидают, и автоматически проверить, соответствуют ли реальные результаты этим ожиданиям.
* Мощные инструменты для работы с [тестовыми данными](/docs/features/test-data/). Создание, удаление и поиск нужных данных теперь не проблема.
* Благодаря поддержке [текучих выражений](/docs/getting-started/fluent-api), код тестов становится более читаемым и понятным, что упрощает его поддержку и модификацию.
* Кроме того, YAxUnit предусматривает возможность расширения функциональности, что позволяет разработчикам адаптировать инструмент под свои уникальные требования.
* Последнее, но не менее важное - [плагин для запуска тестов](/docs/getting-started/edt-plugin/) из 1С:Enterprise Development Tools (EDT). Значительно упрощает процесс написания и отладки тестов. В один клик вы может запустить и увидеть результат нужного теста.
Самостоятельное расширение для написания и выполнения модульного тестирования.
Все эти возможности делают YAxUnit мощным помощником разработчика на непростом пути тестирования.
### Возможности
![Отчет о тестировании](images/reports.png)
- YAxUnit - это расширение с открытым исходным кодом, которое используется для написания и выполнения тестов
- Разрабатывалось с оглядкой на JUnit5, пожалуй, лучший фреймворк тестирования
- Предоставляет движок выполнения тестов
- Предоставляет утверждения для проверки ожидаемых результатов
- Тесты могут быть организованы в наборы и выполняться в разных контекстах
- Позволяет быстрее и проще не только писать, но и читать тесты
- Результаты тестирования могут быть сохранены в отчет, на текущий момент jUnit и json.
- Большая часть пользовательского API реализована как [текучие выражения](https://ru.wikipedia.org/wiki/Fluent_interface)
- Предусмотрена возможность расширения функциональности, можно регистрировать свои форматы отчетов, добавлять модули с утверждениями
- Реализован [плагин для EDT](https://github.com/bia-technologies/edt-test-runner), который упрощает процесс запуска тестов
Подробнее ознакомиться с функциональностью вы можете изучив [документацию](/docs/user-api).
А для того, что бы начать писать тесты необходимо [установить расширение](/docs/install) в свою IDE (конфигуратор или EDT).
## Пример тестового модуля
Для создания теста нужно в расширении (в отдельном или в том же) добавить модуль, содержащий экспортный метод регистрации - `ИсполняемыеСценарии` и реализовать тесты.
Пример модуля тестов:
```bsl
#Область СлужебныйПрограммныйИнтерфейс
Процедура ИсполняемыеСценарии() Экспорт
// Регистрация тестов
ЮТТесты // Регистрация тестов выполняет через модуль регистратор
.ДобавитьТестовыйНабор("Математические методы") // Набор - объединение тестов
.ДобавитьТест("Сложение") // Обязательно при регистрации указываем имя экспортного метода
.ДобавитьТест("Вычитание", "Вычитание") // Также можно указать представление теста
.ДобавитьТест("Вычитание", "ВычитаниеСервер", , "Сервер") // Контекст исполнения, по умолчанию тест выполняется во всех контекстах модуля
.ДобавитьКлиентскийТест("УмножениеНаКлиенте") // Есть отдельный метод для регистрации клиентских тестов
.ДобавитьСерверныйТест("ДелениеНаСервере", "Деление на сервер") // Есть отдельный метод для регистрации серверных тестов
.ДобавитьТестовыйНабор("Строковые методы")
.ДобавитьТест("СтрНайти")
.ДобавитьТест("СтрРазделить");
КонецПроцедуры
#КонецОбласти
#Область Тесты
Процедура Сложение() Экспорт
// Реализация теста на сложение
ЮТест.ОжидаетЧто(2 + 3, "2 + 3") // Используя модуль утверждений установим проверяемое значение и пояснение
.ИмеетТип("Число") // Проверим тип
.Заполнено() // Заполненность проверяемого значения
.Больше(0) // Сравним с нулем
.Равно(5); // Проверим ожидаемый результат
ЮТест.ОжидаетЧто(-8 + 8, "-8 + 8") // Проверим второй вариант
.Равно(0);
КонецПроцедуры
Процедура Вычитание() Экспорт
// Реализация теста на вычитание
ЮТест.ОжидаетЧто(2 - 3, "2 - 3").ИмеетТип("Число").Заполнено().Меньше(0);
КонецПроцедуры
#КонецОбласти
#Область События
// Также в тесте можно обрабатывать события выполнения
// Например можно реализовать подготовку и удаление тестовых данных
// Зачистку временных файлов, настройку системы
Процедура ПередВсемиТестами() Экспорт
// Выполняется перед запуском всех тестов контекста, те если есть тесты и на клиенте и на сервер, то метод будет выполнен 2 раза
Сообщить("Запуск тестирования");
КонецПроцедуры
Процедура ПередТестовымНабором() Экспорт
// Выполняется перед каждым тестовым набором для каждого контекста выполнения
Контекст = ЮТест.КонтекстТестовогоНабора(); // Контекст набора служит для хранения любых данных, нужных при тестировании
// Контекст живет в рамках контекста выполнения,
// таки образом, через контекст нельзя передавать данные между серверными и клиентскими тестами
Контекст.Вставить("ВремяНачала", ТекущаяУниверсальнаяДатаВМиллисекундах());
КонецПроцедуры
Процедура ПередКаждымТестом() Экспорт
// Выполняется перед каждым тестом
Контекст = ЮТест.КонтекстТеста(); // Контекст теста служит для хранения любых данных, нужных при тестировании
// Контекст создает перед тестом и уничтожается после его выполнения
// В контекст например, можно помещать созданные в процессе данные, что бы потом их удалить
Контекст.Вставить("ВремяНачала", ТекущаяУниверсальнаяДатаВМиллисекундах());
КонецПроцедуры
Процедура ПослеКаждогоТеста() Экспорт
// Выполняется после каждого теста
Контекст = ЮТест.КонтекстТеста();
Сообщить("Время выполнения теста: " + (ТекущаяУниверсальнаяДатаВМиллисекундах() - Контекст.ВремяНачала));
КонецПроцедуры
Процедура ПослеТестовогоНабора() Экспорт
// Выполняется после каждого тестового набора для каждого контекста выполнения
// Применяется для очистки данных и т.д.
Контекст = ЮТест.КонтекстТестовогоНабора();
Сообщить("Время выполнения набора: " + (ТекущаяУниверсальнаяДатаВМиллисекундах() - Контекст.ВремяНачала));
КонецПроцедуры
Процедура ПослеВсехТестов() Экспорт
// Выполняется после выполнения всех тестов контекста, те если есть тесты и на клиенте и на сервер, то метод будет выполнен 2 раза
// В этом событии все контексты уже уничтожены
Сообщить("Тестирование завершено");
КонецПроцедуры
#КонецОбласти
```
После запуска тестов модуля в EDT (используя [плагин](https://github.com/bia-technologies/edt-test-runner)) получаем такой отчет:
![Отчет](images/report.png)
## Запуск
### Запуск из EDT
При разработке в EDT процесс запуска тестов можно упростить, установив [плагин](https://github.com/bia-technologies/edt-test-runner)
и настроив конфигурацию запуска, как указано в описании плагина.
### Запуск вне EDT
Для запуска тестов без использования EDT необходимо:
1. Сформировать файл конфигурации запуска [вручную](/docs/run), либо воспользоваться [формой настройки](/docs/yaxunit-ui)
2. [Запустить 1С:Предприятие](/docs/run) с параметром `RunUnitTests=ПутьКФайлуКонфигурации.json`.
Чтобы более подробнее ознакомиться с возможностями YAxUnit вы можете изучить [документацию](/docs/features), либо [установив](/docs/getting-started/install) все необходимое? попробуйте написать [первый тест](/docs/getting-started/first-test)
## Благодарности

View File

@ -0,0 +1,23 @@
import siteConfig from '@generated/docusaurus.config';
export default function prismIncludeLanguages(PrismObject) {
const {
themeConfig: {prism},
} = siteConfig;
const {additionalLanguages} = prism;
// Prism components work on the Prism instance on the window, while prism-
// react-renderer uses its own Prism instance. We temporarily mount the
// instance onto window, import components to enhance it, then remove it to
// avoid polluting global namespace.
// You can mutate PrismObject: registering plugins, deleting languages... As
// long as you don't re-assign it
globalThis.Prism = PrismObject;
additionalLanguages.forEach((lang) => {
if (lang === 'php') {
// eslint-disable-next-line global-require
require('prismjs/components/prism-markup-templating.js');
}
// eslint-disable-next-line global-require, import/no-dynamic-require
require(`prismjs/components/prism-${lang}`);
});
delete globalThis.Prism;
}

View File

@ -35,7 +35,7 @@
// ПараметрыМетода = Мокито.МассивПараметров(ПараметрыПодключения, Ресурс, HTTPМетод, Параметры, ОписаниеТела, Заголовки);
//
// ПрерватьВыполнение = Ложь;
// Результат = Мокито.АнализВызова(РаботаСHTTP, "ВыполнитьЗапрос", ПараметрыМетода, ПрерватьВыполнение);
// Результат = МокитоПерехват.АнализВызова(РаботаСHTTP, "ВыполнитьЗапрос", ПараметрыМетода, ПрерватьВыполнение);
//
// Если НЕ ПрерватьВыполнение Тогда
// Возврат ПродолжитьВызов(ПараметрыПодключения, Ресурс, HTTPМетод, Параметры, ОписаниеТела, Заголовки);
@ -238,8 +238,9 @@
#Область СлужебныйПрограммныйИнтерфейс
// Анализ вызова.
// Deprecate
#Область УстаревшиеПроцедурыИФункции
// Устарела. Анализ вызова.
//
// Параметры:
// Объект - Произвольный
@ -251,8 +252,12 @@
// Произвольный - Подменный результат работы метода
Функция АнализВызова(Объект, ИмяМетода, ПараметрыМетода, ПрерватьВыполнение) Экспорт
ЮТМетодыСлужебный.ВызовУстаревшегоМетода("Мокито.АнализВызова", "МокитоПерехват.АнализВызова", "24.03");
Возврат МокитоСлужебный.АнализВызова(Объект, ИмяМетода, ПараметрыМетода, ПрерватьВыполнение);
КонецФункции
#КонецОбласти
#КонецОбласти

View File

@ -0,0 +1,37 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//©///////////////////////////////////////////////////////////////////////////©//
#Область ПрограммныйИнтерфейс
// Анализ вызова.
//
// Параметры:
// Объект - Произвольный
// ИмяМетода - Произвольный
// ПараметрыМетода - Произвольный
// ПрерватьВыполнение - Произвольный
//
// Возвращаемое значение:
// Произвольный - Подменный результат работы метода
Функция АнализВызова(Объект, ИмяМетода, ПараметрыМетода, ПрерватьВыполнение) Экспорт
Возврат МокитоСлужебный.АнализВызова(Объект, ИмяМетода, ПараметрыМетода, ПрерватьВыполнение);
КонецФункции
#КонецОбласти

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<mdclass:CommonModule xmlns:mdclass="http://g5.1c.ru/v8/dt/metadata/mdclass" uuid="fe844e14-a070-45e6-bbdf-fa9eacaf83f2">
<name>МокитоПерехват</name>
<synonym>
<key>ru</key>
<value>Мокито перехват</value>
</synonym>
<server>true</server>
</mdclass:CommonModule>

View File

@ -273,7 +273,7 @@
// Генерирует случайную дату в будущем.
// Максимальное значение генерируемой даты можно ограничить параметрами.
// Например: СлучайнаяДатаВБудущем(2, "часа") - будет сформирована дата в интервали (ТекущаяДата, ТекущаяДата + 2 часа]
// Например: СлучайнаяДатаВБудущем(2, "часа") - будет сформирована дата в интервале (ТекущаяДата, ТекущаяДата + 2 часа]
//
// Параметры:
// Интервал - Число - Интервал
@ -295,7 +295,7 @@
// Генерирует случайную дату в прошлом.
// Минимальное значение генерируемой даты можно ограничить параметрами.
// Например: СлучайнаяДатаВПрошлом(2, "часа") - будет сформирована дата в интервали [ТекущаяДата - 2 часа, ТекущаяДата)
// Например: СлучайнаяДатаВПрошлом(2, "часа") - будет сформирована дата в интервале [ТекущаяДата - 2 часа, ТекущаяДата)
//
// Параметры:
// Интервал - Число - Интервал
@ -317,7 +317,7 @@
// Генерирует случайную дату, значение которой больше указанной.
// Максимальное значение генерируемой даты можно ограничить параметрами.
// Например: СлучайнаяДатаПосле(Дата, 2, "часа") - будет сформирована дата в интервали [Дата - 2 часа, Дата)
// Например: СлучайнаяДатаПосле(Дата, 2, "часа") - будет сформирована дата в интервале [Дата - 2 часа, Дата)
//
// Параметры:
// Дата - Дата
@ -347,7 +347,7 @@
// Генерирует случайную дату, значение которой меньше указанной.
// Минимальное значение генерируемой даты можно ограничить параметрами.
// Например: СлучайнаяДатаПосле(Дата, 2, "часа") - будет сформирована дата в интервали [Дата - 2 часа, Дата)
// Например: СлучайнаяДатаПосле(Дата, 2, "часа") - будет сформирована дата в интервале [Дата - 2 часа, Дата)
//
// Параметры:
// Дата - Дата

View File

@ -338,7 +338,7 @@
// Представление - Строка
// ИспользуяИмя - Булево - Необходимо ли использовать имя объекта при формировании представления.
// Ложь - По умолчанию, представление будет установлено по параметру.
// Истина - Новое представление объекта = "<ИмяОбъект>. <Параметр Представление>"
// Истина - Новое представление объекта = `<ИмяОбъект>. <Параметр Представление>`
//
// Возвращаемое значение:
// ОбщийМодуль - Этот же модуль

View File

@ -822,10 +822,11 @@
#Область ПроверкаМетодов
// Проверяет установленный для проверки метод Контекста на то, что метод упадет по исключению, далее текст исключения проверяется на ожидаемый.
// Вызывает метод (см. Метод) объекта контекста и проверяет, выбрасывает ли он исключение.
// Проверяет, что метод упадет по исключению, а текст исключения содержит(включает) указанный.
//
// Параметры:
// ОжидаемоеЗначение - Строка - Ожидается, что сообщение об ошибке будет содержать данный текст
// ОжидаемоеЗначение - Строка - Ожидается, что сообщение об ошибке будет содержать(включать) данный текст
// ОписаниеПроверки - Строка - Описание конкретной проверки
// ВТранзакции - Булево - Вызов метода выполняется в трананзакции
//
@ -859,11 +860,13 @@
КонецФункции
// Проверяет установленный для проверки метод Контекста на то, что метод не упадет по исключению
// или упадет по исключению текст которого не содержит ожидаемый.
// Вызывает метод (см. Метод) объекта контекста и проверяет, выбрасывает ли он исключение.
// Проверяет, что метод не упадет по исключению
// или упадет по исключению текст, которого не содержит указанный.
//
// Параметры:
// ОжидаемоеЗначение - Строка - Ожидается, что сообщение об ошибке НЕ будет содержать данный текст
// ОжидаемоеЗначение - Строка - Ожидается, что метод выбросит исключение, текст которого НЕ будет содержать (включать) данный текст
// - Неопределено - Ожидается, что метод отработает без выбора исключения
// ОписаниеПроверки - Строка - Описание конкретной проверки
// ВТранзакции - Булево - Вызов метода выполняется в трананзакции
//

View File

@ -53,6 +53,7 @@
<commonTemplates>CommonTemplate.ЮТОписаниеМетаданных</commonTemplates>
<commonModules>CommonModule.Мокито</commonModules>
<commonModules>CommonModule.МокитоОбучение</commonModules>
<commonModules>CommonModule.МокитоПерехват</commonModules>
<commonModules>CommonModule.МокитоПроверки</commonModules>
<commonModules>CommonModule.МокитоСлужебный</commonModules>
<commonModules>CommonModule.ЮТАсинхроннаяОбработкаСлужебныйКлиент</commonModules>

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -44,9 +44,9 @@ Var Body;
// Возвращаемое значение:
// ДвоичныеДанные - Тело
Function GetBodyAsBinaryData() Export
BodyType = TypeOf(Body);
If Body = Undefined Then
Return GetBinaryDataFromBase64String("");
ElsIf BodyType = Type("BinaryData") Then
@ -54,7 +54,7 @@ Function GetBodyAsBinaryData() Export
ElsIf BodyType = Type("String") Then
Return GetBinaryDataFromString(Body);
EndIf;
EndFunction
// Возвращает тело как поток.
@ -62,22 +62,22 @@ EndFunction
// Возвращаемое значение:
// Поток - Тело
Function GetBodyAsStream() Export
Return GetBodyAsBinaryData().OpenStreamForRead();
EndFunction
// Возвращает тело как строку.
//
// Параметры:
// Кодировка - КодировкаТекста, Строка - Кодировка
// Encoding - КодировкаТекста, Строка - Кодировка
//
// Возвращаемое значение:
// Строка - Тело
Function GetBodyAsString(Encoding = Undefined) Export
BodyType = TypeOf(Body);
If Body = Undefined Then
Return "";
ElsIf BodyType = Тип("BinaryData") Then
@ -85,37 +85,37 @@ Function GetBodyAsString(Encoding = Undefined) Export
ElsIf BodyType = Тип("String") Then
Return Body;
EndIf;
EndFunction
// Устанавливает тело как двоичные данные.
// При получении тела в другом формате происходит конвертация
//
// Параметры:
// Данные - ДвоичныеДанные
// Data - ДвоичныеДанные
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function УстановитьТелоКакДвоичныеДанные(Data) Export
Body = Data;
Return ThisObject;
EndFunction
// Устанавливает тело как строку.
// При получении тела в другом формате происходит конвертация
//
// Параметры:
// Строка - Строка
// String - Строка
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function УстановитьТелоКакСтроку(String) Export
Body = String;
Return ThisObject;
EndFunction
// Устанавливает тело как строку JSON.
@ -123,107 +123,107 @@ EndFunction
// При получении тела в другом формате происходит конвертация
//
// Параметры:
// Данные - Произвольный
// Data - Произвольный
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function УстановитьТелоКакСтрокуJSON(Data) Export
JSONWriter = Новый JSONWriter();
JSONWriter.SetString();
WriteJSON(JSONWriter, Data);
Body = JSONWriter.Close();
Return ThisObject;
EndFunction
// Добавляет заголовок.
//
// Параметры:
// ИмяЗаголовка - Строка
// Значение - Строка
// HeaderName - Строка
// Value - Строка
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function ДобавитьЗаголовок(HeaderName, Value) Export
Headers.Insert(HeaderName, Value);
Return ThisObject;
EndFunction
// Добавляет параметр запроса.
//
// Параметры:
// ИмяПараметра - Строка
// Значение - Строка
// ParameterName - Строка
// Value - Строка
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function ДобавитьПараметрЗапроса(ParameterName, Value) Export
QueryOptions.Insert(ParameterName, Value);
Return ThisObject;
EndFunction
// Добавляет параметр URL.
//
// Параметры:
// ИмяПараметра - Строка
// Значение - Строка
// ParameterName - Строка
// Value - Строка
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function ДобавитьПараметрURL(ParameterName, Value) Export
URLParameters.Insert(ParameterName, Value);
Return ThisObject;
EndFunction
// Устанавливает HTTP метод
//
// Параметры:
// Значение - Строка - Имя http метода
// Value - Строка - Имя http метода
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function Метод(Value) Export
HTTPMethod = Value;
Return ThisObject;
EndFunction
// Устанавливает базовый URL.
//
// Параметры:
// Значение - Строка - базовый URL
// Value - Строка - базовый URL
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function БазовыйURL(Value) Export
BaseURL = Value;
Return ThisObject;
EndFunction
// Устанавливает относительный URL.
//
// Параметры:
// Значение - Строка - относительный URL
// Value - Строка - относительный URL
//
// Возвращаемое значение:
// ОбработкаОбъект.ЮТHTTPСервисЗапрос - Мок
// ОбработкаОбъект.ЮТHTTPServiceRequest - Мок
Function ОтносительныйURL(Value) Export
RelativeURL = Value;
Return ThisObject;
EndFunction
#КонецОбласти
@ -231,14 +231,14 @@ EndFunction
#Область СлужебныеПроцедурыИФункции
Процедура Initialize()
HTTPMethod = "GET";
BaseURL = "";
Headers = New Map();
RelativeURL = "";
URLParameters = New Map();
QueryOptions = New Map();
КонецПроцедуры
#КонецОбласти
@ -249,4 +249,4 @@ Initialize();
#КонецОбласти
#КонецЕсли
#КонецЕсли

View File

@ -77,7 +77,7 @@
// ОбработкаОбъект.ЮТКонструкторОбъектаXDTO - Конструктор
Функция Фикция(ИмяРеквизита) Экспорт
Свойство = ТекущийТип.Свойства.Получить(ИмяРеквизита);
Свойство = Свойство(ИмяРеквизита);
Значение = СлучайноеЗначениеСвойства(Свойство, 0);
Установить(ИмяРеквизита, Значение);
@ -154,7 +154,7 @@
// ОбработкаОбъект.ЮТКонструкторОбъектаXDTO - Добавить новый
Функция ДобавитьНовый(ИмяРеквизита) Экспорт
Свойство = ТекущийТип.Свойства.Получить(ИмяРеквизита);
Свойство = Свойство(ИмяРеквизита);
Если НЕ ЭтоТипОбъектаXDTO(Свойство.Тип) Тогда
ВызватьИсключение СтрШаблон("Метод применяется только для свойств-объектов. Реквизит: %1 имеет тип %2", ИмяРеквизита, Свойство.Тип);
@ -203,6 +203,19 @@
#Область СлужебныеПроцедурыИФункции
Функция Свойство(ИмяСвойства)
Свойство = ТекущийТип.Свойства.Получить(ИмяСвойства);
Если Свойство = Неопределено Тогда
Сообщение = СтрШаблон("Тип XDTO `%1` не содержит свойства `%2`", ТекущийТип.Имя, ИмяСвойства);
ВызватьИсключение Сообщение;
КонецЕсли;
Возврат Свойство;
КонецФункции
Процедура ДобавитьНовыйОбъектВСтек(Тип)
Объект = НовыйОбъект(Тип);

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
ПараметрыМетода = Мокито.МассивПараметров(ОбрабатываемыеЭлементы);
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(Справочники.Встречи, "СохранитьИзменения", ПараметрыМетода, ПрерватьВыполнение);
Результат = МокитоПерехват.АнализВызова(Справочники.Встречи, "СохранитьИзменения", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(ОбрабатываемыеЭлементы);
@ -38,7 +38,7 @@
ПараметрыМетода = Мокито.МассивПараметров(Объект, ОбрабатываемыйЭлемент);
ПрерватьВыполнение = Ложь;
Мокито.АнализВызова(Справочники.Встречи, "ЗаполнитьОбъект", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(Справочники.Встречи, "ЗаполнитьОбъект", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Объект, ОбрабатываемыйЭлемент);

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
ПараметрыМетода = Новый Массив();
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ЭтотОбъект, "УказанКорректныйПериод", ПараметрыМетода, ПрерватьВыполнение);
Результат = МокитоПерехват.АнализВызова(ЭтотОбъект, "УказанКорректныйПериод", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов();
@ -38,7 +38,7 @@
ПараметрыМетода = Мокито.МассивПараметров(Отказ);
ПрерватьВыполнение = Ложь;
Мокито.АнализВызова(ЭтотОбъект, "ПередЗаписью", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(ЭтотОбъект, "ПередЗаписью", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Отказ);

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
ПараметрыМетода = Мокито.МассивПараметров(ТабличныйДокумент);
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ЭтотОбъект, "ПечатнаяФормаШтрихкода", ПараметрыМетода, ПрерватьВыполнение);
Результат = МокитоПерехват.АнализВызова(ЭтотОбъект, "ПечатнаяФормаШтрихкода", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(ТабличныйДокумент);

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
ПараметрыМетода = Мокито.МассивПараметров(Адрес, ФорматОтвета, Авторизация);
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(Интеграция, "ВыполнитьЗапрос", ПараметрыМетода, ПрерватьВыполнение);
Результат = МокитоПерехват.АнализВызова(Интеграция, "ВыполнитьЗапрос", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(Адрес, ФорматОтвета, Авторизация);
@ -38,7 +38,7 @@
ПараметрыМетода = Новый Массив;
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(Интеграция, "Методы", ПараметрыМетода, ПрерватьВыполнение);
Результат = МокитоПерехват.АнализВызова(Интеграция, "Методы", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов();

View File

@ -472,7 +472,7 @@
.КоличествоВызовов("ВыполнитьЗапрос", Мокито.МассивПараметров(ЛюбойПараметр, Новый Структура("Флаг", 2))).Равно(1)
КонецПроцедуры
Процедура МокированиеЦепочкиВызовов() Экспорт
Процедура МокированиеЦепочкиВызовов() Экспорт
Мокито.Обучение(Интеграция)
.Когда("ВыполнитьЗапрос")
@ -489,6 +489,7 @@
.Равно(3);
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(""))
.Равно(3);
КонецПроцедуры
#КонецОбласти

View File

@ -130,6 +130,10 @@
Процедура ДобавитьНовый() Экспорт
Объект = НовыйКонструктор()
.ДобавитьНовый("Товары")
.Фикция("Цена")
.Фикция("Количество")
.ПерейтиКВладельцу()
.ДобавитьНовый("Товары")
.Фикция("Цена")
.Фикция("Количество")
@ -137,7 +141,7 @@
ЮТест.ОжидаетЧто(Объект)
.ИмеетТип(Тип("ОбъектXDTO"))
.Свойство("Товары").ИмеетДлину(1)
.Свойство("Товары").ИмеетДлину(2)
.Свойство("Товары[0].Цена").Заполнено()
.Свойство("Товары[0].Сумма").НеЗаполнено();

View File

@ -1,3 +1,20 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//©///////////////////////////////////////////////////////////////////////////©//
&Вместо("ПередЗаписьюДокумента")
Процедура Расш1_ПередЗаписьюДокумента(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
@ -7,7 +24,7 @@
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ПодпискиНаСобытия, "ПередЗаписьюДокумента", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(ПодпискиНаСобытия, "ПередЗаписьюДокумента", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда
@ -24,7 +41,7 @@
// Отправляем данные на анализ
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(ПодпискиНаСобытия, "ПередЗаписьюСправочника", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(ПодпискиНаСобытия, "ПередЗаписьюСправочника", ПараметрыМетода, ПрерватьВыполнение);
// Обрабатываем результат анализа
Если НЕ ПрерватьВыполнение Тогда

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@
Процедура Расш1_ОпределитьТекущегоПользователя() Экспорт
ПрерватьВыполнение = Ложь;
Результат = Мокито.АнализВызова(Пользователи, "ОпределитьТекущегоПользователя", Новый Массив, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(Пользователи, "ОпределитьТекущегоПользователя", Новый Массив, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов();

View File

@ -1,3 +1,20 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//©///////////////////////////////////////////////////////////////////////////©//
&Вместо("ЗаполнитьСписок")
Процедура Расш1_ЗаполнитьСписок() Экспорт
@ -5,7 +22,7 @@
ПараметрыМетода = Новый Массив();;
ПрерватьВыполнение = Ложь;
Мокито.АнализВызова(ЭтотОбъект, "ЗаполнитьСписок", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(ЭтотОбъект, "ЗаполнитьСписок", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов();

View File

@ -1,6 +1,6 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2023 BIA-Technologies Limited Liability Company
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@
ПараметрыМетода = Мокито.МассивПараметров(Отказ, Режим);
ПрерватьВыполнение = Ложь;
Мокито.АнализВызова(ЭтотОбъект, "ОбработкаПроведения", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(ЭтотОбъект, "ОбработкаПроведения", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(Отказ, Режим);

View File

@ -1,3 +1,20 @@
//©///////////////////////////////////////////////////////////////////////////©//
//
// Copyright 2021-2024 BIA-Technologies Limited Liability Company
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//©///////////////////////////////////////////////////////////////////////////©//
&Вместо("ЗаполненоКорректно")
Процедура Расш1_ЗаполненоКорректно(НаборЗаписей) Экспорт
@ -5,7 +22,7 @@
ПараметрыМетода = Мокито.МассивПараметров(НаборЗаписей);
ПрерватьВыполнение = Ложь;
Мокито.АнализВызова(РегистрыСведений.ЦеныТоваров, "ЗаполненоКорректно", ПараметрыМетода, ПрерватьВыполнение);
МокитоПерехват.АнализВызова(РегистрыСведений.ЦеныТоваров, "ЗаполненоКорректно", ПараметрыМетода, ПрерватьВыполнение);
Если НЕ ПрерватьВыполнение Тогда
ПродолжитьВызов(НаборЗаписей);

Some files were not shown because too many files have changed in this diff Show More