Mock-тестування
При написанні тестів спеціалістам часто потрібно взаємодіяти із зовнішніми системами, такими як бази даних, веб-сервіси або сторонні API. Щоб ізолювати тестований код від цих залежностей та створити надійні та ефективні тести, можна використовувати моки.
Моки дозволяють замінити реальні об'єкти на контрольовані імітації, щоб створити передбачуване та ізольоване оточення для тестування. Вони допомагають скоротити час на розробку та тестування, а також знизити ризики під час ведення бізнесу.
У статті ми визначимо характеристики та переваги мока, зупинимося на його інструментах та видах заглушок, а також розглянемо приклад використання моків під час тестування.
Що таке моки у тестуванні
МОК (mock) — це техніка у програмуванні, коли створюються штучні об'єкти чи функції, які імітують реальні. Так можна тестувати код незалежно від зовнішніх ресурсів чи компонентів, що робить проведення тестів надійнішим і передбачуваним.
Моки дозволяють створювати сценарії, які складно відтворити у реальному середовищі. Це допомагає перевірити поведінку ПЗ у різних умовах, включаючи граничні значення та помилкові сценарії.
приклад. Існує функція, яка надсилає запит на сервер для отримання даних. Ми можемо використовувати моки, щоб імітувати відповідь сервера без фактичного звернення до нього. Таким чином, фахівці можуть перевірити, як функція обробляє дані без ризику залежності від статусу сервера.
У яких випадках можна використовувати моки:
Ізоляція залежностей
У деяких випадках QA-фахівцям потрібно ізолювати код, що тестується, від зовнішніх залежностей, таких як бази даних або мережеві запити, адже вони можуть уповільнити або ускладнити тестування.
Негативне тестування
За допомогою мок тестувальники можуть створювати сценарії з помилками або недоступними реальними компонентами для перевірки поведінки системи.
Тестування ще не готових компонентів
Моки можуть бути використані для тестування частин системи, які ще не готові для використання.
Тестування сценаріїв, що відтворюються
Якщо тестувальники створюють сценарії, що відтворюються, для перевірки специфічних умов роботи ІТ-продукту, моки можуть допомогти контролювати ці сценарії.
Тестування взаємодії
Моки перевіряють правильну взаємодію компонентів системи, навіть якщо реальні компоненти ще готові чи недоступні.
Тестування кешування та часу
Моки допомагають при тестуванні кешування даних або часових залежностей, дозволяючи контролювати час та послідовність викликів.
Тестування масштабованості
При тестуванні масштабованості системи тестувальники використовують моки для імітації великої кількості взаємодій та перевірки того, чи система витримує навантаження.
Тестування конфігурацій
Моки можуть бути корисні при тестуванні різних конфігурацій системи, що дозволяє легко змінювати параметри для перевірки різних сценаріїв.
Види заглушок
- Стаби (Stubs) – найпростіші заглушки, які повертають заздалегідь визначені значення на вхідні дані.
- Моки (Mocks) — складніші заглушки, які контролюють виклик методів, передачу аргументів і перевіряють очікувану поведінку об'єкта, що тестується.
- Фейки (Fakes) – спрощені об'єкти, які імітують поведінку реальних компонентів. Реалізація у фейків спрощена.
- Спайі (Spies) - об'єкти, які записують інформацію про своє використання. Це можуть бути передані аргументи та кількість викликів. Так тестувальники можуть аналізувати їхню поведінку після виконання тестів.
Інструменти для mock-тестування
Зараз ми розглянемо основні інструменти, які можна застосовувати під час mock-тестування.
Postman використовується для тестування API.
- Зрозумілий інтерфейс
- Безкоштовний
- Моки можна приховати
- Зручно генерувати одноразові моки
- Безкоштовний
- Можна приховати моки
- Моки можуть вивантажуватись у готовий код
- МОК генерується за документацією
- Безкоштовний
- Є панель керування UI
- Можна розділяти права під час роботи
- Застосовується на проектах для створення моків SOAP, HTTP
- Не потрібна реєстрація для роботи
- Багатофункціональний
- Потужна утиліта
- Можна створювати моки для REST/SOAP
- Зрозумілий інтерфейс
- Працює локально
- Можна використовувати з OpenApi
- Зрозуміла документація
- Безкоштовний
- Багатофункціональний
- Є багато доопрацювань сторонніми розробниками
- Зрозумілий інтерфейс
- Багатий функціонал
- Підтримує різні формати запуску
- Хороша документація
Переваги та недоліки мок-тестування
Переваги використання моків у тестуванні:
- Гнучкість. Мокування дозволяє гнучко налаштовувати поведінку об'єктів для тестування бізнес-проектів. QA-фахівці можуть створювати тест-кейси, які покривають різні варіанти роботи програмного забезпечення.
- Ізоляція. За допомогою мока можна ізолювати компонент, що тестується. Це допомагає проводити тестування окремих модулів.
- Швидкість. З моками зазвичай можна працювати швидше, ніж із реальними об'єктами чи сервісами.Завдяки цьому можна швидше випустити ІТ-продукт ринку.
- Передбачуваність. Використання мок дозволяє створювати передбачувані умови для тестування. Задаючи поведінку моків, можна контролювати результати тестів.
- Зручність. Моки можна легко створювати та налаштовувати за допомогою спеціальних бібліотек та інструментів, що робить процес тестування зручнішим.
- Скорочення витрат за тестування. Тестувальники можуть створювати ефективні та швидкі тести без необхідності підтримувати та налаштовувати реальні зовнішні системи. Завдяки цьому компанія може знизити витрати на перевірку якості програмного забезпечення.
- Підтримка паралельного та автоматизованого тестування. Моки полегшують створення паралельних та автоматизованих тестів, тому що дозволяють контролювати взаємодію між компонентами системи та поведінку імітованих об'єктів.
- Підвищення рівня тестового покриття. Використання моків допомагає збільшити рівень тестового покриття коду, оскільки дозволяє створювати тести для різних сценаріїв та умов. Завдяки цьому можна перевірити якість ПЗ.
- Спрощення виправлення та аналізу помилок. Розробники можуть контролювати поведінку об'єктів, що імітуються, і вивчати взаємодію між компонентами системи. Так їм легко виправляти дефекти.
Недоліки використання моків у тестуванні:
- Недостатнє відображення реальної системи. Моки імітують об'єкти, можуть повністю відображати реальне поведінка системи. Іноді це призводить до недооцінки помилок у ПЗ.
- Може не знайти непередбачуваних помилок. Моки не завжди здатні створювати всі можливі сценарії роботи системи.
- Додаткова робота. Створення та підтримка моків може вимагати додаткового часу та зусиль з боку розробників.
- Ризик виникнення помилок. Неправильно налаштовані моки можуть призвести до помилкових результатів та помилок у процесі розробки.
- Залежність бібліотек. Для створення моків фахівці зазвичай використовують спеціалізовані бібліотеки. Тому вони можуть залежати від конкретних інструментів, що ускладнить процес розробки.
Приклад використання моків
Візьмемо для прикладу програму, яка відображає інформацію про книги з бази даних.
Також можна переконатися, що при натисканні на книгу відкривається докладна інформація і працює сортування списку книг.
Моки відіграють важливу роль у тестуванні. Моки - це заглушки, які контролюють виклик методів, передачу аргументів та перевірку очікуваної поведінки системи. Доцільність застосування моків залежить від цілей тестування.
Використовуючи моки, фахівці можуть зосередитися на тестуванні конкретної функціональності, не переймаючись зовнішніми залежностями. Це допомагає покращити якість ПЗ та прискорити процес розробки.
Важливо пам'ятати, що для правильного використання мок потрібно добре розуміти принципи тестування та архітектури програмного забезпечення. Якщо у вас залишилися питання на цю тему, ви можете поставити їх нашим QA-фахівцям на безкоштовну консультацію.
Моки - JS: Просунуте тестування
У тестуванні дуже популярний мокінг. Технічно він схожий на стабінг, і тому їх часто плутають (спеціально чи ненавмисно). Але все ж таки вони служать різним цілям і використовуються в різних ситуаціях. Розберемося, що це таке, і коли він потрібний.
До цього моменту ми розглядали побічні ефекти як перешкоду для тестування нашої логіки.Для їхньої ізоляції використовувалися стаби або пряме вимкнення логіки у тестовому середовищі. Після цього можна було спокійно перевіряти правильність роботи функції.
У деяких ситуаціях потрібно щось інше. Не результат роботи функції, а те, що вона виконує потрібну нам дію, наприклад, надсилає правильний HTTP-запит з правильними параметрами. Для цього знадобляться моки. Моки перевіряють, як виконується код.
HTTP
import nock
from
'nock';
import
getPrivateForkNames
>
from
'../src.js';
// Запобігання випадковим запитам
nock.disableNetConnect();
test('getPrivateForkNames',
async
()
=>
// Повна назва домену
const
scope
=
nock('https://api.github.com')
// Повний шлях
.get('/orgs/hexlet/repos/?private=true')
.reply(200,
[ fork:
true,
name:
'one'
>,
fork:
false,
name:
'two'
>]);
await
getPrivateForkNames('hexlet');
// Метод `scope.isDone()` повертає `true` тільки тоді,
// коли відповідний запит було виконано всередині `getPrivateForkNames`
expect(scope.isDone()).toBe(true);
>);
Це називається мокінг. Мок перевіряє, що якийсь код виконався певним чином. Це може бути виклик функції, HTTP-запит тощо. Завдання мока переконатися, що це сталося, і у тому, як це сталося, наприклад, що у функцію було передано конкретні дані.
Що нам дає така перевірка? В даному випадку мало що. Так, ми переконуємось, що виклик був, але саме собою це ще ні про що не говорить. То коли ж корисні моки?
Уявіть, що ми розробляли б бібліотеку @octokit/rest, ту саму, що виконує запити до GitHub API. Вся суть цієї бібліотеки полягає в тому, щоб виконати правильні запити з правильними параметрами. Тому потрібно обов'язково перевіряти виконання запитів із зазначенням точних URL-адрес.Тільки в такому випадку можна бути впевненим, що вона виконує правильні запити.
У цьому ключова відмінність мока від стаба. Стаб усуває побічний ефект, щоб не заважати перевірці результату роботи коду, наприклад, повернення даних із функції. Мок фокусується на тому, як конкретно працює код, що він робить усередині. При цьому суто технічно мок і стаб створюються однаково, за винятком того, що на мок вішають очікування, що перевіряють виклики. Це призводить до плутанини, бо часто моками називають стаби. З цим нічого вже не вдієш, але для себе завжди намагайтеся зрозуміти, про що йдеться. Це важливо від цього залежить фокус тестів.
Функції
Моки досить часто використовують із функціями (методами). Наприклад, вони можуть перевіряти:
- Що функція була викликана
- Скільки разів вона була викликана
- Які аргументи ми використали
- Скільки аргументів було передано у функцію
- Що повернула функція
Припустимо, ми хочемо протестувати функцію forEach . Вона викликає колбек для кожного елемента колекції:
[1,
2,
3].forEach((v)
=>
console.log(v));
// або простіше [1, 2, 3]. forEach (console.log)
Ця функція нічого не повертає, тому її прямо не протестувати. Можна спробувати зробити це за допомогою моків. Перевіримо, що вона викликає переданий колбек та передає туди потрібні значення.
Оскільки ми вивчаємо Jest, то для створення моків скористаємося вбудованим механізмом Jest. В інших фреймворках можуть бути свої вбудовані механізми. Крім того, як ми переконалися вище, існують спеціалізовані бібліотеки для моків та стабів.
test('forEach',
()
=>
// Моки функцій у Jest створюються за допомогою функції jest.fn
// Вона повертає функцію, яка запам'ятовує всі свої виклики та передані аргументи
// Потім це використовується для перевірок
const
callback
=
jest.fn();
[1,
2,
3].forEach(callback);
// Тепер перевіряємо, що вона була викликана з правильними аргументами потрібну кількість разів
expect(callback.mock.calls).доHaveLength(3);
// Перший аргумент першого виклику
expect(callback.mock.calls[0][0]).toBe(1);
// Перший аргумент другого виклику
expect(callback.mock.calls[1][0]).toBe(2);
// Перший аргумент третього виклику
expect(callback.mock.calls[2][0]).toBe(3);
>);
За допомогою моків ми перевірили, що функція була викликана рівно три рази, і їй послідовно для кожного виклику передавався новий елемент колекції. У принципі можна сказати, що цей тест дійсно перевіряє працездатність функції forEach() . Але чи можна це зробити простіше, без мока і без зав'язки на внутрішню поведінку? Виявляється, можна. Для цього достатньо використовувати замикання:
test('forEach',
()
=>
const
result
=
[];
const
numbers
=
[1,
2,
3];
numbers.forEach((x)
=>
result.push(x));
expect(result).toEqual(numbers);
>);
Об'єкти
Jest дозволяє створювати моки для об'єктів. Вони створюються за допомогою функції jest.spyOn(), що нагадує вже відому нам jest.fn(). Ця функція приймає на вхід об'єкт та ім'я методу в цьому об'єкті, а також відстежує виклики цього методу. Відстежимо, як приклад, виклик console.log() :
test('logging something',
()
=>
const
spy
=
jest.spyOn(console,
'log');
console.log(12);
expect(spy).доHaveBeenCalled();
// => true, т.к. метод log був викликаний
expect(spy).доHaveBeenCalledTimes(1);
// => true, т.к. метод був викликаний 1 раз
expect(spy).доHaveBeenCalledWith(12);
// true, т.к. log був викликаний з аргументом 12
expect(spy.mock.calls[0][0]).toBe(12);
// Перевірка, ідентична попередньої
>);
Крім того, Jest дозволяє створювати свою реалізацію збору даних про виклики функції, що відстежується за допомогою методу mockImplementation(fn) . Колбеком цього методу буде функція, яка виконується після кожного виклику функції, що відстежується. Візьмемо попередній приклад, але тепер зберемо аргументи кожного виклику console.log() в окремий масив:
test('logging something',
()
=>
const
logCalls
=
[];
// Тут функція всередині mockImplementation приймає на вхід аргумент(и),
// з яким був викликаний console.log, і зберігає їх у заздалегідь створений масив
const
spy
=
jest.spyOn(console,
'log').mockImplementation((.
args)
=>
logCalls.push(args));
console.log('one');
console.log('two');
expect(logCalls.join('
')).toBe('one two');
>);
Переваги та недоліки
Незважаючи на те, що існують ситуації, в яких потрібні моки, в більшості ситуацій їх потрібно уникати. Моки дуже багато знають про те, як працює код. Будь-який тест із моками із чорної скриньки перетворюється на білу скриньку. Повсюдне використання моків призводить до двох речей:
- Після рефакторингу доводиться переписувати тести (багато тестів!) навіть якщо код працює правильно. Відбувається це через зав'язку на те, як конкретно працює код
Відкрити доступ
Курси програмування для новачків та досвідчених розробників. Почніть навчання безкоштовно
- 130 курсів, 2000+ годин теорії
- 1000 практичних завдань у браузері
- 360 000 студентів
Наші випускники працюють у компаніях:
Що таке мокування
Мокування - це техніка заміни реальних об'єктів на "заглушки" або "муляжі" для зручності тестування коду.
Моки та мокування – це техніки, що використовуються у тестуванні програмного забезпечення, щоб ізолювати код, який тестується, від інших компонентів, з якими він взаємодіє. Вони особливо корисні, коли необхідно протестувати код, що залежить від зовнішніх сервісів, баз даних або інших сторонніх ресурсів. Давайте розберемо докладніше.
Мокування простими словами
Мокування - це техніка заміни реальних об'єктів на "заглушки" або "муляжі" для зручності тестування коду. Замість підключення до реальної бази даних, сервера або зовнішнього API, ви використовуєте моки, які заздалегідь налаштовані повертати потрібні дані. Це дозволяє протестувати код швидко і без складних налаштувань, начебто справжні залежності вже працювали.
Що таке моки?
МОК (від англ. mock, «Підробка») - це об'єкт, який імітує поведінку реального об'єкта в додатку, але при цьому не містить логіки цього об'єкта. Моки створюють для того, щоби тестувати компоненти в ізоляції від інших залежностей.
Приклад: якщо ваш код взаємодіє із зовнішнім API для отримання даних, ви можете використовувати мок, щоб підробити цей API і заздалегідь задати очікувані відповіді. Тож ви зможете протестувати логіку роботи з даними без реального звернення до API.
Що таке мокування?
Мокування - це процес створення моків та налаштування їх поведінки. Мокування дозволяє:
- Контролювати поведінку залежностей: ви можете точно вказати, як мок поводитиметься в кожному тесті, які дані він повинен повертати або які винятки викидати.
- Ізолювати тестований код: моки замінюють реальні об'єкти та запобігають небажаним викликам або побічним ефектам, пов'язаним з ними (наприклад, доступ до бази даних).
- Тестувати різні сценарії: за допомогою моків можна легко змоделювати різні ситуації, включаючи ті, які складно відтворити в реальному середовищі, наприклад помилки мережі.
Бібліотеки для мокування в Swift та Kotlin
У Swift та Kotlin також є популярні бібліотеки для мокування, які допомагають розробникам тестувати код із використанням заглушок для залежності. Ось основні з них:
Бібліотеки для мокування в Swift
- Cuckoo
- Одна з найпопулярніших бібліотек для мокування у Swift.
- Дозволяє автоматично генерувати моки для класів та протоколів.
- Підтримує як синхронні, і асинхронні методи, зручна для тестування складних сценаріїв.
- Mockito-swift
- Подібна до Java-бібліотеки Mockito, але адаптована для Swift.
- Підтримує такі функції, як перевірка дзвінків, створення заглушок для методів та тестування різних сценаріїв.
- Проста та зручна у використанні для базових моків.
- Mockingbird
- Сучасна та потужна бібліотека для мокування у Swift.
- Підтримує автоматичну генерацію моків та може інтегруватися з Xcode.
- Забезпечує гнучкість під час налаштування поведінки моків та підтримує «expectations» для перевірки викликів.
- Sourcery
- Не зовсім бібліотека для мокування, але використовується в Swift для генерації коду, у тому числі моків.
- Підходить для створення кастомних заглушок та економить час за рахунок генерації коду.
Бібліотеки для мокування в Kotlin
- Mockito-Kotlin
- Версія популярної Java-бібліотеки Mockito адаптована для Kotlin.
- Дозволяє легко створювати моки, налаштовувати поведінку функцій та перевіряти їх виклики.
- Добре інтегрується з Kotlin-кодом і підтримує котлин-специфічні функції, такі як nullable типи та розширення.
- MockK
- Потужна бібліотека для мокування, спеціально розроблена для Kotlin.
- Підтримує об'єктні моки (наприклад, для Singleton та об'єктів-компаньйонів), що відрізняє її від багатьох інших бібліотек.
- Дозволяє легко створювати заглушки, налаштовувати значення, що повертаються, і перевіряти виклики.
- Зручна для роботи з Kotlin-корутинами, що особливо важливо у сучасних Android-додатках.
- Kotlin Test
- Вбудована бібліотека тестування для Kotlin, яка також підтримує створення простих моків та заглушок.
- Підходить для базового мокування, але для складніших випадків краще використовувати MockK або Mockito-Kotlin.
Ці бібліотеки дозволяють Swift і Kotlin ефективно створювати тести, ізолюючи тестований код від залежностей і підвищуючи надійність результатів.
Чи є мокування антипаттерном
Мокування саме по собі не є антипаттерномПроте неправильне або надмірне використання моків може призвести до проблем і стати антипаттерном. У деяких випадках це знижує якість тестів та робить їх менш корисними. Давайте розберемо, коли мокування може вважатися антипаттерном і в чому полягають ризики його неправильного застосування.
Коли мокування корисне
Мокування виправдане, коли:
- Потрібно ізолювати код від зовнішніх залежностей, таких як бази даних, мережі, API та ін.
- Бажаєте протестувати поведінку коду в різних сценаріях, включаючи помилки та винятки.
- Є необхідність прискорити виконання тестів, виключивши складні та довгі залежності.
- Потрібно перевірити окремі методи чи класи, які залежить від інших складних компонентів.
У цих випадках моки допомагають зробити тести більш передбачуваними, швидкими та стійкими.
Коли мокування стає антипаттерном
Мокування може вважатися антипаттерном, якщо:
- Надмірне використання моків: коли моки використовуються занадто часто або для всіх залежностей, це може призвести до створення тестів, які перевіряють не реальну поведінку системи, а лише узгодженість моків.
- Мокування складної логіки: якщо моки створюються для компонентів із складною бізнес-логікою, ви фактично підміняєте справжні дані «бутафорією». Це може спричинити ситуацію, коли тести проходять успішно, але реальні помилки залишаються непоміченими.
- Складні та тендітні тести: якщо моки створюються із жорсткою прив'язкою до внутрішніх деталей реалізації, при зміні цих деталей тести починають ламатися. Це робить тести «тендітними» і важко підтримуються.
- Ускладнення підтримки тестів: якщо моки активно налаштовуються за допомогою складних заглушок і перевірок, тести стають важкочитаними та заплутаними. Це ускладнює їхню підтримку та ускладнює розуміння.
- Недостатнє тестування інтеграції: надмірне захоплення моками призводить до нестачі інтеграційних тестів, у яких перевіряється взаємодія компонентів. У результаті навіть якщо юніт-тести проходять, система може виявитися непрацездатною при реальній інтеграції.
Найкращі практики використання мокування
Щоб уникнути перетворення мокування на антипаттерн:
- Використовуйте моки лише там, де це необхідно. Наприклад, для ізоляції зовнішніх ресурсів, а чи не для заміни внутрішньої логіки докладання.
- Пишіть інтеграційні тести для перевірки роботи реальних компонентів разом. Юніт-тести з моками не повинні замінювати інтеграційні випробування.
- Тестуйте поведінку, а не реалізацію: фокусуйтеся на тому, що код повинен робити, а не на тому, як він це робить Це зменшить залежність від внутрішніх деталей реалізації.
- Мінімізуйте кількість моків в одному тесті: намагайтеся тримати кожен тест максимально простим і зрозумілим, щоб за зміни залежностей не потрібно було змінювати весь тест.
Таким чином, мокування може бути потужним інструментом, якщо використовувати його розумно. Проблеми починаються, коли моки використовуються без необхідності або в надто великій кількості – у цьому випадку тести можуть стати антипаттерном.
Якщо ви знайшли помилку - виділіть її та натисніть Ctrl+Enter! Для зв'язку з нами можна використовувати [email protected].
