Skip to content

Библиотека для создания моков и стабов в стиле mockito

License

Notifications You must be signed in to change notification settings

nixel2007/moskito

Repository files navigation

Moskito - mock this script!

CI Статус порога качества Технический долг

Библиотека предназначена для создания моков (mock) и стабов (stub) в OneScript. За основу взят фреймворк mockito для Java.

Цели создания библиотеки

Периодически в тестировании возникают задачи проверки работы модулей или классов, имеющих внешние зависимости. Этими зависимостями могут быть как безобидные вспомогательные классы (например, библиотеки cmdline, logos), так и зависимости, требующие определенного "внешнего" состояния. Например, Файл или HTTPСоединение.

Для возможности эмуляции работы таких классов и была создана эта библиотека.

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

Эмуляция работы http-соединения

HTTPЗапрос = Новый HTTPЗапрос("/ping");

МокСоединение = Мок.Получить(Новый HTTPСоединение("localhost"));

// Вызов метода Получить по умолчанию вернет NULL, без исключений о недоступности сервиса
Результат = МокСоединение.Получить(HTTPЗапрос);
Ожидаем.Что(Результат).Равно(NULL);

// Но можно вызвать исключение
МокСоединение.Когда().Получить(HTTPЗапрос).ТогдаВыбрасываетИсключение(Новый ИнформацияОбОшибке("Ресурс недоступен", Новый Структура));
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(HTTPЗапрос);
Ожидаем.Что(МокСоединение).Метод("Получить", МассивПараметров).ВыбрасываетИсключение("Ресурс недоступен");

// Начальные свойства мокируемого объекта переносятся в сам мок
Ожидаем.Что(МокСоединение.Сервер).Равно("localhost");

// Можно переопределить результат работы метода
МокСоединение.Когда().Получить(HTTPЗапрос).ТогдаВозвращает("Переопределенный ответ");
Результат = МокСоединение.Получить(HTTPЗапрос);
Ожидаем.Что(Результат).Равно("Переопределенный ответ");

// Некоторые объекты нельзя создавать через конструктор.
// Для таких объектов можно воспользоваться созданием мока из типа.
Ответ = Мок.Получить(Тип("HTTPОтвет"));
Ответ.КодСостояния = 200;
Ответ.Когда().ПолучитьТелоКакСтроку().ТогдаВозвращает("Переопределенный ответ");

// Моки можно вкладывать в моки
МокСоединение.Когда().Получить(HTTPЗапрос).ТогдаВозвращает(Ответ);
Результат = МокСоединение.Получить(HTTPЗапрос);
Ожидаем.Что(Результат.ПолучитьТелоКакСтроку()).Равно("Переопределенный ответ");

// При этом вызов метода с другими параметрами продолжит возвращать NULL
Результат = МокСоединение.Получить("/test");
Ожидаем.Что(Результат).Равно(NULL);

Использование матчеров

Для переопределения результатов методов не обязательно передавать точные значения параметров. Можно использовать "матчеры". Матчер - это специальная функция, которая проверяет, что параметр удовлетворяет какому-то условию.

МокОбъект = Мок.Получить(Тип("Структура"));

// Заставим структуру для проверки всех свойств возвращать "Истина"
МокОбъект.Когда().Свойство(Матчеры.ЛюбаяСтрока()).ТогдаВозвращает(Истина);

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

// Вызов функции, например, с числом, продолжит возвращать NULL
Ожидаем.Что(МокОбъект.Свойство("ДругаяСтрока")).Равно(NULL);

Матчер - это функция, которая должна вернуть Истина или Ложь. Вы можете написать матчер любой сложности, используся специальный конструктор.

// Функция-матчер принимает минимум один параметр
//  Значение - Произвольный - само проверяемое значение
// 
// В функции могут содержаться дополнительные параметры. Значения таких параметров должны передаваться
// в виде массива при создании матчера.
//
Функция БольшеТрех(Знач Значение) Экспорт
    Возврат Значение > 3;
КонецФункции

МокОбъект = Мок.Получить(Тип("Структура"));

СвойМатчер = Новый Матчер(ЭтотОбъект, "БольшеТрех");
МокОбъект.Когда().Свойство(СвойМатчер).ТогдаВозвращает(Истина);

Ожидаем.Что(МокОбъект.Свойство(0)).Равно(NULL);
Ожидаем.Что(МокОбъект.Свойство(3)).Равно(NULL);
Ожидаем.Что(МокОбъект.Свойство(4)).Равно(Истина);

Переопределение с использованием Ответов

В сложных тестовых случаях методов ТогдаВозвращает и ТогдаВыбрасываетИсключение может не хватать. Вы можете реализовать более сложную логику ответа с использованием метода ТогдаОтвечает

// Допустим у нас есть функция, которая просто возвращает строковую информацию
// о вызванном методе.
 
Функция ВернутьИнформациюОВызове(Знач ИнформацияОВызове) Экспорт

    // Параметр ИнформацияОВызове хранит данные о самом моке, вызываемом методе,
    // и массиве параметров, с которым вызвали этот метод.
	МокОбъект = ИнформацияОВызове.Мок();
	ИмяМетода = ИнформацияОВызове.ИмяМетода();
	Параметры = ИнформацияОВызове.Параметры();

    // Преобразование массива в строку.
	Параметры = ПроцессорыКоллекций.ИзКоллекции(Параметры).ВСтроку(", ");

    // Произовльное возвращаемое значение
	Возврат СтрШаблон("Вызван метод <%1> с параметрами <%2>", ИмяМетода, Параметры);
 
КонецФункции

// Создадим указатель на функцию, которая должна вызываться при "ответе"
Ответ = Новый Ответ(ЭтотОбъект, "ВернутьИнформациюОВызове");

МокОбъект = Мок.Получить(Тип("Структура"));
// Когда у мока вызывается метод Свойство(), срабатывает вызов функции, заложенной в "Ответе" 
МокОбъект.Когда().Свойство(Матчеры.ЛюбаяСтрока(), Матчеры.ЛюбоеЗначение()).ТогдаОтвечает(Ответ);

// Вызовем функцию
Результат = МокОбъект.Свойство("Поле");

// В "Результате" получим строку, возвращенную функцией ВернутьИнформациюОВызове
Ожидаем.Что(Результат).Равно("Вызван метод <Свойство> с параметрами <Поле, >");

Проверка вызова методов

// Допустим у нас есть класс, принимающий в себя HTTPСоединение, и вызывающий у него метод Получить()
// Проверим, что метод Получить() действительно вызывался

МойКласс = Новый МойКласс();
МокСоединение = Мок.Получить(Новый HTTPСоединение("localhost"));

МойКласс.УстановитьКонтекст(МокСоединение);
МойКласс.ВызватьМетодКоторыйДолженДернутьПолучить("/ping");

// Если метод Получить не вызывался, будет выдано исключение
МокСоединение.ПроверитьЧтоВызывалсяМетод().Получить("/ping");

Режим "шпиона" (spy)

// Создаем нового шпиона над Структурой
МокСтруктура = Мок.Следить(Новый Структура);

// Шпион вызывает реальные методы объекта
МокСтруктура.Вставить("Ключ1", 1);

// Методы "внутреннего" объекта работают как обычно
Ожидаем.Что(МокСтруктура.Количество()).Равно(1);

МокСтруктура.Вставить("Ключ2", 2);

// Однако, мы можем все еще можем переопределить (stub) результат работы любого метода
МокСтруктура.Когда().Количество().ТогдаВозвращает(999);

// В этом случае будет возвращаться наше значение.
Ожидаем.Что(МокСтруктура.Количество()).Равно(999);

Имеющиеся ограничения

  • В силу ограничений движка на создание методов с именами, совпадающими с именами глобальных методов, не от всех объектов можно создать идентичный мок. Например, если создать мок от Массив, то метод Найти(), совпадающий по имени с глобальным методом, будет переименован в метод _Найти(). Структура же замокается без проблем.