# Мокито Мокито - модуль созданный по образу популярного java-фреймворка для тестирования [Mockito](https://site.mockito.org/). Расширяет возможности тестирования, позволяет легко менять логику работы системы подменяя результаты работы методов, отключая какие-либо алгоритмы и проверки. Юнит-тесты, это тесты конкретных методов в отрыве от системы - контролировать данные используемые методом, изменение логики и ошибки других объектов не должны аффектить на тест. В реальных конфигурациях объекты тесно связаны друг с другом, поэтому добиться контроля влияющих данных очень сложно. Обычно приходится создавать большой объем тестовых данных. А добиться изоляции от изменения логики других объектов почти невозможно. Мокирование же позволяет изменить логику работы системы таким образом, чтобы тестируемый метод не вызывал другие методы и использовал уже подготовленные данные. Например, для тестирования проведения реализации товаров мы можем подменить результат функции формирующей таблицу проводок и избежать сложной подготовки данных. С помощью Мокито разработчик указывает, что делать при вызове определенных методов - вернуть нужный результат, вызвать исключение или просто не трогать ненужные методы. После тестирования разработчик может запросить и проверить статистику о вызовах, как и какие методы были вызваны. Пример: ```bsl ОтветСерверы = ОтветУспешногоЗапроса("Серверы"); ОтветДиски = ОтветУспешногоЗапроса("Диски"); Мокито.Обучение(РаботаСHTTP) .Когда(РаботаСHTTP.ВыполнитьЗапрос(ПараметрыПодключения, "/hosts", "GET")) .Вернуть(ОтветСерверы) .Когда(РаботаСHTTP.ВыполнитьЗапрос(ПараметрыПодключения, "/disks", "GET")) .Вернуть(ОтветДиски) .Прогон(); Результат = БиллингДрайверГипервизорNutanix.Серверы(ПараметрыПодключения); ``` В этом примере изменяется работа модуля `РаботаСHTTP`, для функции `ВыполнитьЗапрос`, вызванной с нужными параметрами будет возвращено подготовленное значение, а сам метод не будет вызван. Мы получим ожидаемые ответы на запросы к сторонней системе и уйдет от проблем связанных с ней - недоступность, изменение контрактов и т.д. Работа с Мокито делится на 3 стадии: 1. Обучение - настраиваем поведение методов системы 2. Прогон - выполнение теста целевого метода 3. Проверка - анализ вызовов ## Использование ### Настройка мокируемых методов Для работы Мокито вам необходимо добавить интересующие методы в тестовое расширение. Эта позволит управлять поведением метода: * подменять результат во время выполнения теста * использовать явный вызов метода с параметрами на стадии обучения, например `Мокито.Обучение(Справочники.ИсточникиДанных).Когда(Справочники.ИсточникиДанных.СохраненныеБезопасныеДанные(Справочник)).Вернуть(Результат)` * использовать явный вызов метода с параметрами на стадии проверки, например `Мокито.Проверить(Справочники.ИсточникиДанных).КоличествоВызовов(Справочники.ИсточникиДанных.СохраненныеБезопасныеДанные(Справочник)).Больше(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й и последующий параметры имеют значения по умолчанию. * Проверяем собранные вызовы: * `Заполнено` - есть вызовы метода по указанным условиям * `Пусто` - нет вызовов метода по указанным условиям * `Равно` - количество вызовов попавших под условия равно указанному значению * `Больше` - количество вызовов попавших под условия больше указанного значения * `Меньше` - количество вызовов попавших под условия меньше указанного значения ## Кейсы использования\* \* *В примерах опускается часть добавления метода в расширение* 1. Подмена результат функции для любого вызова ```bsl Мокито.Обучение(РаботаСHTTP) .Когда("ОтправитьОбъектНаСервер") .Вернуть(1) ``` 2. Выключение алгоритма проведения документа ```bsl Мокито.Обучение(СсылкаИлиОбъектДокумент) .Когда("ОбработкаПроведения") .Пропустить() ``` 3. Выбросить исключение, если в метод передан некорректный набор параметров ```bsl Мокито.Обучение(РаботаСHTTP) .Когда("ОтправитьОбъектНаСервер") .ВыброситьИсключение("Не верные параметры вызова") .Когда(РаботаСHTTP.ОтправитьОбъектНаСервер(Справочники.ИсточникиДанных.FTP, Мокито.ЛюбойПараметр())) .ВыполнитьМетод(); ```