Мокито
Мокито — это модуль, созданный по образцу популярного Java-фреймворка для тестирования Mockito. Он расширяет возможности тестирования, позволяя легко изменять поведение системы, подменяя результаты работы методов, отключая алгоритмы и проверки.
Что такое мокирование?
Мокирование — это техника, которая позволяет заменить реальное поведение методов на заранее заданное. Это полезно, когда:
- Вы хотите протестировать код, который зависит от внешних систем или сложных алгоритмов.
- Вы хотите убедиться, что методы вызываются с правильными параметрами.
- Вы хотите отключи ть часть логики, чтобы сосредоточиться на тестировании конкретного участка кода.
Возможности Мокито
С помощью Мокито вы можете:
- Изменять поведение методов: Указывать, в каких случаях методы должны возвращать определенные значения, выбрасывать исключения или вообще не выполняться.
- Настраивать условия: Задавать условия для параметров методов, чтобы изменения применялись только в нужных случаях.
- Мокировать приватные и экспортные методы: Мокировать как публичные, так и приватные методы объектов, документов, справочников и других элементов.
- Собирать статистику: Наблюдать за вызовами методов, чтобы убедиться, что они вызываются с правильными параметрами.
ТелоОтвета = Новый Структура("success": Истина);
// Настройка мокирования
Мокито.Обучение(РаботаСHTTP)
.Когда("ОтправитьЗапрос").Вернуть(
ЮТест.Данные().HTTPОтвет()
.УстановитьКодСостояния(200)
.УстановитьТело(ТелоОтвета))
.Прогон();
// Тестовый прогон
Результат = РаботаСHTTP.ОтправитьЗапрос("https://example.com");
// Проверка
ЮТест.ОжидаетЧто(Результат.КодСостояния).Равно(200);
Мокирование доступно для любых методов конфигурации (или почти любых):
- Методы общих модулей
- Методы менеджеров
- Методы объектов (документов, справочников и т.д.)
- Методы отчетов и обработок
- События объектов конфигурации
- Методы модулей прочих объектов конфигурации
Что Мокито не может?
- Выполнять произвольную логику при в ызове метода.
- Изменять параметры методов.
- Хранить результаты вызовов при сборе статистики.
Как работает Мокито?
Мокито работает на основе двух ключевых механизмов:
- Расширение методов: Методы конфигурации заменяются (
&Вместо
) на специальные обработчики, которые позволяют управлять их поведением. - Глобальный контекст: Все настройки и статистика вызовов хранятся в глобальном контексте, что позволяет получить к ним доступ как из теста (для настройки), так и из замененных методов (для анализа).
Мокито работает через заимствование методов конфигурации, что накладывает определенные ограничения. Подмена поведения возможна только для методов конфигурации. Это означает, что мокирование недоступно для:
- Методов платформы (встроенных методов 1С).
- Методов расширений.
- Методов внешних обработок или отчетов.
Таким образом, Мокито эффективно работает только в рамках методов, определенных в самой конфигурации. Это важно учитывать при проектировании тестов и выборе подходов к тестированию.
Детально принципы работы Мокито описаны в отдельной статье. Она поможет вам глубже разобраться в механизмах мокирования, понять, как работает подмена методов, настройка поведения и сбор статистики вызовов. Это полезно для тех, кто хочет максимально эффективно использовать Мокито в своих тестах и лучше понять его внутреннюю логику.
Подготовка
В первую очередь, необходимо добавить мокируемый метод в расширение конфигурации. Это можно сделать с помощью стандартной команды IDE "Добавить метод в расширение". После этого нужно добавить шаблонный код для мокирования.
Для упрощения процесса:
- В EDT доступны специальные команды, которые автоматически добавляют необходимый код для моков.
- В конфигураторе можно использовать готовые шаблоны, чтобы быстро настроить мокирование.
Шаблоны заимствованных методов
-
Для функции:
&Вместо("<ИмяМетода>")
Функция Мок_<ИмяМетода>(<ПараметрыМетода>)
ПараметрыМетода = Мокито.МассивПараметров(<ПараметрыМетода>);
ПрерватьВыполнение = Ложь;
Результат = МокитоПерехват.АнализВызова(<Объект>, "<ИмяМетода>", ПараметрыМетода, ПрерватьВыполнение);
Если Не ПрерватьВыполнение Тогда
Возврат ПродолжитьВызов(<ПараметрыМетода>);
Иначе
Возврат Результат;
КонецЕсли;
КонецФункции -
Для процедуры:
&Вместо("<ИмяМетода>")
Процедура Мок_<ИмяМетода>(<ПараметрыМетода>)
ПараметрыМетода = Мокито.МассивПараметров(<ПараметрыМетода>);
ПрерватьВыполнение = Ложь;
МокитоПерехват.АнализВызова(<Объект>, "<ИмяМетода>", ПараметрыМетода, ПрерватьВыполнение);
Если Не ПрерватьВыполнение Тогда
ПродолжитьВызов(<ПараметрыМетода>);
КонецЕсли;
КонецПроцедуры
Параметры:
<ИмяМетода>
— Имя мокируемого метода.<ПараметрыМетода>
— Параметры мокируемого метода.<Объект>
— Объект-владелец метода:- Для общих модулей — ссылка на общий модуль (например,
ОбщегоНазначения
). Лучше не использоватьЭтотОбъект
. - Для объектов (справочники, документы, наборы, формы) —
ЭтотОбъект
. - Для менеджеров — ссылка на менеджер (например,
Справочники.Пользователи
).
- Для общих модулей — ссылка на общий модуль (например,
Если метод не имеет параметров, можно использовать пустой массив:
ПараметрыМетода = Новый Массив;
Этапы тестирования с Мокито
После добавления заимствованного метода-перехватчика в расширение конфигурации, вы можете начинать писать тесты с использованием Мокито для этого метода.
В общем случае тестирование с использованием Мокито состоит из трех этапов:
-
Обучение:
На этом этапе настраивается мокирование — указывается, какие методы нужно мокировать, при каких условиях (параметрах) и как они должны себя вести. Вы задаете поведение моков, например, возврат определенных значений, выброс исключений или пропуск выполнения. -
Прогон:
На этом этапе выполняется тестовый метод с применением настроенных моков. Вызывается проверяемая функция, которая использует мокируемые методы. В процессе выполнения вместо реальных методов используются их моки, что позволяет изолировать тестируемую логику. -
Проверка:
На завершающем этапе анализируются вызовы методов, чтобы убедиться, что все работает как ожидается. Проверяется, сколько раз методы были вызваны, с какими параметрами и в правильной ли последовательности.
Пример:
// Подготовка
Ответ = ЮТест.Данные().HTTPОтвет()
.УстановитьКодСостояния(200)
.УстановитьТело(Новый Структура("id, status", "9999", "delivered"));
// Обучение Мокито
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("УстановитьСоединение").Вернуть(Истина)
.Когда("ПослатьСообщение").Вернуть(Ответ)
.Прогон();
// Тестовый прогон
РоботОтправки.ОтправкаСМС();
// Проверка статистики
Мокито.Проверить(От правкаСМС_Провайдер1)
.КоличествоВызовов("ПослатьСообщение")
.Равно(1);
Обучение мокирования
Обучение — это процесс настройки Мокито. Вы указываете, какие методы и при каких условиях должны изменить свое поведение.
Основные методы обучения
-
Когда():
Указывает, какой метод и с какими параметрами нужно мокировать. Позволяет задать условия для вызова метода. -
Вернуть():
Настраивает метод на возврат определенного значения. Используется для имитации успешного выполнения метода. -
ВыброситьИсключение():
Настраивает метод на выброс исключения. Полезно для тестирования обработки ошибок. -
Пропустить():
Отключает выполнение метода. Используется для имитации пропуска вызова метода. -
Наблюдать():
Собирает статистику по вызовам метода. Позволяет отслеживать, сколько раз метод был вызван и с какими параметрами без изменения его поведения. -
ВыполнитьМетод():
Не выполнять мокирование, а выполнить метод конфигурации. Используется для вызова реального метода вместо мокированного.
Сбор статистики вызовов выполняется для всех мокируемых методов, а не только указанных в Наблюдать()
. Это позволяет анализировать все вызовы, даже если они не были явно настроены для наблюдения.
Пример:
Мокито.Обучение(ОтправкаСМС_Провайдер1)
.Когда("УстановитьСоединение").Пропустить()
.Когда("ПослатьСообщение").Вернуть(Ответ)
.Наблюдать("ОбработатьОтвет")
Проверка вызовов
После прогона теста можно проверить, какие методы были вызваны и с какими параметрами. Для этого используется метод Мокито.Проверить()
.
Пример:
Мокито.Проверить(РаботаСHTTP)
.КоличествоВызовов("ОтправитьОбъектНаСервер")
.Равно(3);
Мокито.Обучение(СуперИнтеграция)
.Наблюдать("СоздатьОбъект")
.Наблюдать("УдалитьОбъект")
.Наблюдать("ОбновитьОбъект")
.Прогон();
СуперИнтеграция.ВыполнитьСинхронизацию();
Мокито.Проверить(СуперИнтеграция)
.КоличествоВызовов("СоздатьОбъект").Равно(1)
.КоличествоВызовов("УдалитьОбъект").Пусто()
.КоличествоВызовов("ОбновитьОбъект").Равно(1);
Примеры использования
Пример 1: Вызов исключения при записи объекта
Рассмотрим пример теста, где с помощью Мокито проверяется обработка исключений при записи документа "Оплата".
// Настраиваем мокирование для метода "ПередЗаписью" документа "Оплата"
Мокито.Обучение(Документы.Оплата)
.Когда("ПередЗаписью") // Указываем, что мокируем метод "ПередЗаписью"
.ВыброситьИсключение("Не удалось записать объект") // Настраиваем метод на выброс исключения
.Прогон(); // Завершаем настройку моков
// Создаем объект документа "Оплата"
Документ = СоздатьДокументОплаты();
// Проверяем, что при вызове метода "Записать" с параметром "Проведение" выбрасывается исключение
ЮТест.ОжидаетЧто(Документ)
.Метод("Записать") // Указываем метод, который должен быть вызван
.Параметр(РежимЗаписиДокумента.Проведение) // Указываем параметр, с которым метод должен быть вызван
.ВыбрасываетИсключение("Не удалось записать объект"); // Ожидаем, что метод выбросит исключение с указанным текстом
- Тест демонстрирует, как можно использовать мокирование для проверки сценариев, связанных с обработкой исключений.
- Метод
ПередЗаписью
мокируется, чтобы имитировать ошибку при записи документа, что позволяет проверить реакцию системы на такие ситуации.
Пример 2: Анализ вызовов метода
Рассмотрим пример теста, где с помощью Мокито настраивается наблюдение за вызовами методов общего модуля СинхронизацияДанных
. В этом тесте мы проверяем, что методы СоздатьОбъект
, УдалитьОбъект
и ОбновитьОбъект
вызываются ожидаемое количество раз в процессе выполнения синхронизации. Это позволяет убедиться, что логика вызовов методов соответствует требованиям.
// Настраиваем мокирование для объекта "СинхронизацияДанных"
Мокито.Обучение(СинхронизацияДанных)
.Наблюдать("СоздатьОбъект") // Начинаем наблюдение за методом "СоздатьОбъект"
.Наблюдать("УдалитьОбъект") // Начинаем наблюдение за методом "УдалитьОбъект"
.Наблюдать("ОбновитьОбъект") // Начинаем наблюдение за методом "ОбновитьОбъект"
.Прогон(); // Завершаем настройку моков
// Выполняем метод, который тестируем
СинхронизацияДанных.ВыполнитьСинхронизацию();
// Проверяем, сколько раз были вызваны наблюдаемые методы
Мокито.Проверить(СинхронизацияДанных)
.КоличествоВызовов("СоздатьОбъект").Равно(1) // Ожидаем, что метод "СоздатьОбъект" был вызван 1 раз
.КоличествоВызовов("УдалитьОбъект").Пусто() // Ожидаем, что метод "УдалитьОбъект" не был вызван ни разу
.КоличествоВызовов("ОбновитьОбъект").Равно(1); // Ожидаем, что метод "ОбновитьОбъект" был вызван 1 раз
-
Наблюдение за методами:
- Используется метод
.Наблюдать()
, чтобы отслеживать вызовы методов без изменения их поведения. - Это позволяет проверить, какие методы были вызваны и сколько раз, не вмешиваясь в их логику.
- Используется метод
-
Проверка количества вызовов:
- Метод
КоличествоВызовов()
используется для проверки, сколько раз был вызван каждый из наблюдаемых методов. - Проверяется, что:
СоздатьОбъект
был вызван 1 раз.УдалитьОбъект
не был вызван ни разу (проверка на пустоту).ОбновитьОбъект
был вызван 1 раз.
- Метод
- Такой подход полезен для проверки корректности логики вызовов методов в процессе выполнения сложных операций.
- Тест позволяет убедиться, что методы вызываются в правильной последовательности и с ожидаемой частотой.
- При необходимости можно проверить с условием на параметры вызова.
Пример 3: Тест передачи параметров с использованием выброса исключения
Рассмотрим пример, где с помощью Мокито настраивается выброс исключений при "некорректных" вызовах методов, разрешаются только корректные вызовы с определенными параметрами, и проверяется, что основной алгоритм выполняется без ошибок и вызывает методы ожидаемое количество раз.
// Определяем текст исключения, которое будет выбрасываться при "некорректных" вызовах
ТекстИсключение = "Некорректный вызов";
// Создаем объект для обновления
СсылкаДляОбновления = СоздатьОбъект(Код);
// Настраиваем мокирование для объекта "СинхронизацияДанных"
Мокито.Обучение(СинхронизацияДанных)
.Когда("СоздатьОбъект").ВыброситьИсключение(ТекстИсключение) // При любом вызове "СоздатьОбъект" выбрасываем исключение
.Когда("УдалитьОбъект").ВыброситьИсключение(ТекстИсключение) // При любом вызове "УдалитьОбъект" выбрасываем исключение
.Когда("ОбновитьОбъект").ВыброситьИсключение(ТекстИсключение) // При любом вызове "ОбновитьОбъект" выбрасываем исключение
.Когда("ОбновитьОбъект", Мокито.МассивПараметров(СсылкаДляОбновления)).ВыполнитьМетод() // Разрешаем вызов "ОбновитьОбъект" только с определенной ссылкой
.Прогон(); // Завершаем настройку моков
// Проверяем, что метод "ВыполнитьСинхронизацию" не выбрасывает исключение
ЮТест.ОжидаетЧто(СинхронизацияДанных)
.Метод("ВыполнитьСинхронизацию").НеВыбрасываетИсключение(); // Ожидаем, что метод выполнится без ошибок
// Проверяем, что метод "ОбновитьОбъект" был вызван ровно 1 раз
Мокито.Проверить(СинхронизацияДанных)
.КоличествоВызовов("ОбновитьОбъект").Равно(1); // Ожидаем, что метод "ОбновитьОбъект" был вызван 1 раз
Особенности
-
Настройка исключений:
- Методы
СоздатьОбъект
,УдалитьОбъект
иОбновитьОбъект
настроены на выброс исключения с текстом"Некорректный вызов"
. - Это позволяет имитировать "некорректные" вызовы и проверять, как система реагирует на такие ситуации.
- Методы
-
Разрешение корректного вызова:
- Для метода
ОбновитьОбъект
добавлено исключение: если метод вызывается с определенной ссылкой (СсылкаДляОбновления
), он выполняется без ошибок. - Это позволяет проверить, что только корректные вызовы проходят успешно.
- Для метода
Пример 4: Условия по параметрам
Пример 5: Предикаты
// Сигнатура проверяемого метода:
// Интеграция.ВыполнитьЗапрос(URL: Строка, Тело: Структура, Строка, Произвольный);
// Определяем адрес для тестирования
Адрес = "service.ru";
// Используем Мокито.ЛюбойПараметр() для указания, что параметр может быть любым
ЛюбойПараметр = Мокито.ЛюбойПараметр();
// Создаем условие для проверки структуры: она должна содержать реквизит "Флаг" со значением 1
УсловиеСтруктура = ЮТест.Предикат()
.ИмеетТип("Структура") // Проверяем, что параметр является структурой
.Реквизит("Флаг").Равно(1) // Проверяем, что реквизит "Флаг" равен 1
.Получить(); // Завершаем создание предиката
// Настраиваем мокирование для метода "ВыполнитьЗапрос"
Мокито.Обучение(Интеграция)
// Если вызов не подошел ни под одно условие, выбрасываем исключение
.Когда("ВыполнитьЗапрос")
.ВыброситьИсключение("Не отработал перехват")
// Условие 1: запрос на любой адрес, в качестве тела передана структура с реквизитом "Флаг" равным 1
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЛюбойПараметр,
УсловиеСтруктура))
.Вернуть(1) // Возвращаем 1, если условие выполнено
// Условие 2: запрос на любой адрес, в качестве тела передана структура с реквизитом "Флаг" равным 2
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЛюбойПараметр,
ЮТест.Предикат()
.Реквизит("Флаг").Равно(2)))
.Вернуть(2) // Возвращаем 2, если условие выполнено
// Условие 3: запрос на любой адрес, в качестве тела передан массив, где первый элемент равен 3
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЛюбойПараметр,
ЮТест.Предикат()
.ИмеетТип("Массив")
.Реквизит(0).Равно(3)))
.Вернуть(3) // Возвращаем 3, если условие выполнено
// Условие 4: запрос на любой адрес, в качестве тела передано значение 4
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЛюбойПараметр,
ЮТест.Предикат().Равно(4)))
.Вернуть(4) // Возвращаем 4, если условие выполнено
// Условие 5: запрос на адрес, содержащий "com", и тело равно 4
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЮТест.Предикат().Содержит("com"),
ЮТест.Предикат().Равно(4)))
.Вернуть(5) // Возвращаем 5, если условие выполнено
// Условие 6: запрос на адрес, содержащий "org", и тело равно 4
.Когда("ВыполнитьЗапрос", Мокито.МассивПараметров(
ЮТест.Предикат().Содержит("org").Получить(),
ЮТест.Предикат().Равно(4)))
.Вернуть(6) // Возвращаем 6, если условие выполнено
.Прогон(); // Завершаем настройку моков
// Проверяем различные сценарии вызова метода "ВыполнитьЗапрос"
// Сценарий 1: запрос с адресом "service.ru" и телом в виде структуры с реквизитом "Флаг" равным 1
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(Адрес, Новый Структура("Флаг", 1)))
.Равно(1); // Ожидаем, что метод вернет 1
// Сценарий 2: запрос с адресом "service.ru" и телом равным 4
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(Адрес, 4))
.Равно(4); // Ожидаем, что метод вернет 4
// Сценарий 3: запрос с адресом "service.com" и телом равным 4
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос("service.com", 4))
.Равно(4); // Ожидаем, что метод вернет 4 (не 5, так как предикат не завершен методом Получить)
// Сценарий 4: запрос с адресом "service.org" и телом равным 4
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос("service.org", 4))
.Равно(6); // Ожидаем, что метод вернет 6
// Сценарий 5: запрос с адресом "service.ru" и телом в виде массива, где первый элемент равен 3
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(Адрес, ЮТКоллекции.ЗначениеВМассиве(3)))
.Равно(3); // Ожидаем, что метод вернет 3
// Сценарий 6: запрос с адресом "service.ru" и телом в виде структуры с реквизитом "Флаг" равным 2
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(Адрес, Новый Структура("Флаг", 2)))
.Равно(2); // Ожидаем, что метод вернет 2
Описание теста
-
Настройка моков:
- Используется
Мокито.Обучение
для настройки поведения методаВыполнитьЗапрос
. - Определены различные условия для вызова метода, каждое из которых возвращает определенное значение.
- Если вызов не соответствует ни одному из условий, выбрасывается исключение.
- Используется
-
Проверка сценариев:
- Тест проверяет различные сценарии вызова метода
ВыполнитьЗапрос
с разными параметрами. - Используются предикаты для проверки структуры, типа данных и значений параметров.
- Тест проверяет различные сценарии вызова метода
-
Особенности:
- Если несколько предикатов используются в условиях параметров, необходимо завершать их методом
Получить
, иначе они не будут работать корректно, пример, сценарий 5, который не отработал. - Тест демонстрирует гибкость мокирования и проверки различных условий.
- Если несколько предикатов используются в условиях параметров, необходимо завершать их методом
Этот тест позволяет убедиться, что метод ВыполнитьЗапрос
корректно обрабатывает различные входные данные и возвращает ожидаемые результаты.
Пример 6: Цепочка вызовов
// Настраиваем мокирование для метода "ВыполнитьЗапрос"
Мокито.Обучение(Интеграция)
.Когда("ВыполнитьЗапрос") // Указываем, что мокируем метод "ВыполнитьЗапрос"
.Вернуть(1) // Первый вызов метода вернет 1
.Вернуть(2) // Второй вызов метода вернет 2
.Вернуть(3) // Третий и все последующие вызовы метода вернут 3
.Прогон(); // Завершаем настройку моков
// Проверяем цепочку вызовов метода "ВыполнитьЗапрос"
// Первый вызов: ожидаем, что метод вернет 1
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(ПроизвольныйАдрес))
.Равно(1); // Проверяем, что результат равен 1
// Второй вызов: ожидаем, что метод вернет 2
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(ПроизвольныйАдрес))
.Равно(2); // Проверяем, что результат равен 2
// Третий вызов: ожидаем, что метод вернет 3
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(ПроизвольныйАдрес))
.Равно(3); // Проверяем, что результат равен 3
// Четвертый вызов: ожидаем, что метод снова вернет 3 (последнее значение в цепочке)
ЮТест.ОжидаетЧто(Интеграция.ВыполнитьЗапрос(ПроизвольныйАдрес))
.Равно(3); // Проверяем, что результат равен 3
Описание теста
-
Настройка моков:
- Используется
Мокито.Обучение
для настройки поведения методаВыполнитьЗапрос
. - Метод настроен на возврат разных значений при каждом вызове: сначала
1
, затем2
, а затем3
для всех последующих вызовов.
- Используется
-
Проверка цепочки вызовов:
- Тест проверяет, что метод
ВыполнитьЗапрос
возвращает зна чения в соответствии с заданной цепочкой. - Первый вызов возвращает
1
, второй —2
, третий и все последующие —3
.
- Тест проверяет, что метод
-
Особенности:
- Тест демонстрирует возможность настройки последовательных возвращаемых значений для одного и того же метода.
- После исчерпания цепочки значений метод продолжает возвращать последнее указанное значение (
3
в данном случае).
Этот тест полезен для проверки сценариев, где метод вызывается несколько раз, и каждое его выполнение должно возвращать разные значения в зависимости от порядка вызова.
Преимущества Мокито
-
Гибкость:
Мокито позволяет мокировать как экспортные, так и приватные методы, а также настраивать условия вызова. Это дает возможность тестировать сложные сценарии, где требуется изоляция отдельных компонентов системы. -
Простота использования:
Интуитивно понятный API позволяет быстро настраивать мокирование. Благодаря текучим выражениям, настройка моков становится простой и читаемой, что ускоряет процесс написания тестов. -
Глобальный контекст:
Все настройки и статистика хранятся в одном месте, что упрощает управление мокированием. Это позволяет легко отслеживать вызовы методов и проверять их корректность.
Заключение
Мокито — это мощный инструмент для управления поведением системы во время тестирования. Его принципы работы основаны на расширении методов конфигурации и использовании глобального контекста для хранения настроек и статистики. Понимание этих принципов поможет вам эффективно использовать Мокито для написания качественных тестов.