Библиотека предназначена для создания моков (mock) и стабов (stub) в OneScript. За основу взят фреймворк mockito для Java.
Периодически в тестировании возникают задачи проверки работы модулей или классов, имеющих внешние зависимости. Этими зависимостями могут быть как безобидные вспомогательные классы (например, библиотеки cmdline
, logos
), так и зависимости, требующие определенного "внешнего" состояния. Например, Файл
или 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");
// Создаем нового шпиона над Структурой
МокСтруктура = Мок.Следить(Новый Структура);
// Шпион вызывает реальные методы объекта
МокСтруктура.Вставить("Ключ1", 1);
// Методы "внутреннего" объекта работают как обычно
Ожидаем.Что(МокСтруктура.Количество()).Равно(1);
МокСтруктура.Вставить("Ключ2", 2);
// Однако, мы можем все еще можем переопределить (stub) результат работы любого метода
МокСтруктура.Когда().Количество().ТогдаВозвращает(999);
// В этом случае будет возвращаться наше значение.
Ожидаем.Что(МокСтруктура.Количество()).Равно(999);
Массив
, то метод Найти()
, совпадающий по имени с глобальным методом, будет переименован в метод _Найти()
. Структура
же замокается без проблем.