Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/nixel2007/moskito

Библиотека для создания моков и стабов в стиле mockito
https://github.com/nixel2007/moskito

oscript-lib oscript-package

Last synced: about 5 hours ago
JSON representation

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

Awesome Lists containing this project

README

        

# Moskito - mock this script!

[![CI](https://github.com/nixel2007/moskito/actions/workflows/main.yml/badge.svg)](https://github.com/nixel2007/moskito/actions/workflows/main.yml)
[![Статус порога качества](https://sonar.openbsl.ru/api/project_badges/measure?project=moskito&metric=alert_status)](https://sonar.openbsl.ru/dashboard?id=moskito)
[![Технический долг](https://sonar.openbsl.ru/api/project_badges/measure?project=moskito&metric=sqale_index)](https://sonar.openbsl.ru/dashboard?id=moskito)

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

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

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

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

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

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

```bsl

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

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

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

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

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

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

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

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

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

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

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

```bsl

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

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

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

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

```

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

```bsl

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

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

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

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

```

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

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

```bsl

// Допустим у нас есть функция, которая просто возвращает строковую информацию
// о вызванном методе.

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

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

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

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

КонецФункции

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

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

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

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

```

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

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

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

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

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

```

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

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

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

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

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

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

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

```

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

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