Макет об'єкта
Макети об'єктів (англ. Mock Object) у об'єктно-орієнтованому програмуванні - це об'єкти, що імітують поведінку справжніх об'єктів контрольованими способами, тобто, реалізують інтерфейси справжніх об'єктів, але не мають власної реальної функціональності. Програміст зазвичай створює макет об'єкта для тестування поведінки деяких інших об'єктів, багато в чому так само, як автомобільний дизайнер використовує манекен, що може рухатися, для імітації динамічної поведінки людини в транспортній аварії.
Де варто використовувати
У модульному тестуванні, фіктивні об'єкти можуть моделювати поведінку складних, реальних (не макетних) об'єктів і, отже, корисні, коли реальний об'єкт недоцільно або неможливо використати в модульному тесті. Якщо об'єкт має будь-яку з наведених нижче характеристик, використання фіктивного об'єкту може бути корисним в цьому випадку:
- надає недетерміновані результати (наприклад, час або температуру);
- є сценарії, які важко створити або відтворити (наприклад, помилка в мережі);
- повільно працює (наприклад, повна база даних, яка повинна бути ініціалізована до початку тестування);
- об'єкт ще не існує або може змінити поведінку;
- об'єкт повинен включати в себе інформацію і методи виключно для цілей тестування (а не для реального використання).
Наприклад, створюється програма, що реагує на показники датчика, що вимірює температуру у приміщенні. Необхідно перевірити, що у програмі виникне попередження, якщо температура у приміщенні впаде нижче деякого рівня. Реально знижувати температуру приміщення - складно та недоцільно. Тому виправданим є створення макету для датчика температури, що надає дані про температуру у такому ж форматі, що і реальний датчик, але програміст має змогу керувати даними, що повертає такий датчик.
Типи макетних об'єктів
Макетні об'єкти можуть лише повертати вказані результати або також запам'ятовувати факт викликів своїх методів, та, якщо у тестовому сценарії, де обов'язково тестований об'єкт мав викликати деякий метод макетного об'єкта, але не зробив цього, повідомляти про невиконання тесту. Також існують макетні об'єкти, що просто реалізують інтерфейс реальних (деякою порожньою реалізацією), і програміст не може керувати ними.
Деталі використання
Макетні об'єкти та реальні об'єкти, які вони імітують, мають однакові інтерфейси, що дозволяє клієнту об'єкта не відчувати різниці, реальні об'єкти він використовує чи фіктивні. Сучасні фреймворки для модульного тестування дозволяють програмісту легко створювати макетні об'єкти заданого інтерфейсу або базового типу, вказувати необхідний результат для викликів методів та властивостей макетних об'єктів, вказувати, які параметри мають бути передані тому чи іншому методу, які методи чи властивості взагалі мають викликатися - факт усіх обов'язкових для тесту викликів методів макетного об'єкту може бути перевірено у модульному тесті.
Засоби автоматизації створення
Для мови програмування C Sharp :
Для мови програмування Python :
Для мови програмування Ruby on Rails :
Застереження щодо використання
Використання макетних об'єктів може прив'язати юніт-тести до фактичної реалізації коду, який проходить випробування. Наприклад, фреймворків для юніт-тестування дозволяють розробнику перевіряти, скільки разів і у якому порядку тестований об'єкт викликає макетний об'єкт; рефакторинг може змінити порядок таких викликів, і, при тому що фактично код залишатиметься робочим, тест буде не пройдено. Такі тести називають крихкими. Це доводить, що макетний об'єкт має імітувати лише зовнішню поведінку справжнього, і не прив'язуватися до деталей внутрішньої реалізації.
Реалізація
Нехай, наша аплікація містить операції із конвертуванням грошей. У ній присутній наступний сервіс.
public interface ICurrencyExchange
{
Task<Money> Convert(Money originalAmount, Currency destinationCurrency);
}
Щоб перевірити роботу логіки, яке залежить на цьому сервісі створимо імітацію необхідної нам поведінки.
var mock = new Mock<ICurrencyExchange>();
mock
.Setup(x => x.Convert(It.IsAny<Money>(), It.Is<Currency>(c => c == Currency.Euro)))
.ReturnsAsync(new Money(10, Currency.Euro));
ICurrencyExchange currencyExchange = mock.Object;
Зв'язок з іншими патернами
- Макет об'єкта та заглушка сервісу часто плутають. Та варто розуміти, що макет об'єкта використовуються для імітації поведінки однієї чи декількох функцій та залежно від умов ця імітація може відрізнятись в той час, як заглушка сервісу замінює цілий сервіс та його реалізація залишається незмінною.