Тут ми повинні розглянути два питання.
Перший полягає в тому, що ви, здається, дивитесь на всі ваші тести з точки зору тестування одиниці. Одиничні тести є надзвичайно цінними, але це не єдині види тестів. Тести насправді можна розділити на кілька різних шарів, від дуже швидких одиничних тестів до менш швидких інтеграційних тестів до ще повільніших тестів прийняття . (Може бути розбито ще більше шарів, як функціональні тести .)
Друга полягає в тому, що ви поєднуєте дзвінки до стороннього коду зі своєю бізнес-логікою, створюючи проблеми з тестуванням і, можливо, роблячи свій код більш крихким.
Тестові одиниці повинні бути швидкими і виконувати їх потрібно часто. Знущання із залежностей допомагає тримати ці тести швидко, але потенційно може ввести дірки в покритті, якщо залежність зміниться, а макет не буде. Ваш код може бути порушений, поки ваші тести все ще зелені. Деякі знущаються бібліотеки будуть попереджати вас про зміну інтерфейсу залежності, інші не можуть.
Інтеграційні тести, з іншого боку, призначені для перевірки взаємодії між компонентами, включаючи сторонні бібліотеки. На цьому рівні тестування не слід використовувати макети, оскільки ми хочемо побачити, як фактично взаємодіють об'єкти. Оскільки ми використовуємо реальні об’єкти, ці тести будуть повільнішими, і ми не будемо виконувати їх майже так часто, як наші тестові одиниці.
Тести прийняття виглядають на ще більш високому рівні, перевіряючи, чи відповідають вимоги до програмного забезпечення. Ці тести працюють проти всієї повної системи, яка буде розгорнута. Ще раз, ніяких глузувань не слід використовувати.
Одне керівництво, яке люди вважають цінним щодо макетів, - це не знущатися над тими, якими ви не володієте . Amazon володіє API на S3, щоб вони могли переконатися, що він не змінюється під ними. У вас, з іншого боку, немає цих гарантій. Тому, якщо ви знущаєтесь над API S3 у своїх тестах, він може змінити і порушити ваш код, тоді як усі ваші тести покажуть зелений колір. Тож як ми з’єднаємо тестовий код, який використовує сторонні бібліотеки?
Ну, ми цього не робимо. Якщо ми будемо слідувати керівництву, ми не можемо знущатися над об’єктами, якими ми не володіємо. Але… якщо ми володіємо своїми прямими залежностями, ми можемо їх знущатися. Але як? Ми створюємо власну обгортку для API S3. Ми можемо зробити так, щоб він був схожий на API S3, або можемо зробити так, щоб він більше відповідав нашим потребам (бажано). Ми навіть можемо зробити це трохи абстрактніше, скажімо, PersistenceService
а не як AmazonS3Bucket
. PersistenceService
буде інтерфейсом з такими методами, як #save(Thing)
і #fetch(ThingId)
типи методів, які ми могли б хотіти бачити (це приклади, можливо, вам потрібні різні методи). Тепер ми можемо реалізувати PersistenceService
навколо API S3 (скажімо а S3PersistenceService
), інкапсулюючи його подалі від нашого коду виклику.
Тепер до коду, який викликає API S3. Нам потрібно замінити ці дзвінки на дзвінки до PersistenceService
об'єкта. Ми використовуємо ін'єкцію залежності, щоб передати наше PersistenceService
в об’єкт. Важливо не просити S3PersistenceService
, а попросити PersistenceService
. Це дозволяє нам замінити реалізацію під час наших тестів.
Весь код, який використовував безпосередньо API S3, зараз використовує наш PersistenceService
, а наш S3PersistenceService
тепер здійснює всі дзвінки до API S3. У наших тестах ми можемо глузувати PersistenceService
, оскільки ми володіємо ним, і використовуємо макет, щоб переконатися, що наш код робить правильні дзвінки. Але тепер це залишає, як протестувати S3PersistenceService
. У нього така ж проблема, як і раніше: ми не можемо перевірити її без виклику до зовнішньої служби. Отже ... ми не тестуємо це. Ми можемо знущатись від залежностей API S3, але це не дасть нам додаткової впевненості. Натомість ми повинні перевірити його на більш високому рівні: тести інтеграції.
Це може здатися трохи занепокоєнням, що ми не повинні перевіряти частину нашого коду, але давайте подивимося на те, що ми зробили. У нас було купі коду в усьому місці, де ми не змогли провести тест, який тепер може бути перевірений через модуль PersistenceService
. У нас безладдя в сторонній бібліотеці приурочено до одного класу впровадження. Цей клас повинен забезпечувати необхідну функціональність для використання API, але не має до нього жодної зовнішньої логіки бізнесу. Тому, як тільки це написано, він повинен бути дуже стійким і не повинен сильно змінюватися. Ми можемо покластися на повільніші тести, які ми не запускаємо так часто, оскільки код стабільний.
Наступним кроком є написання тестів на інтеграцію для S3PersistenceService
. Їх слід відокремити за назвою чи папкою, щоб ми могли запустити їх окремо від наших швидких тестових модулів. Інтеграційні тести часто можуть використовувати ті ж рамки тестування, що й одиничні тести, якщо код є достатньо інформативним, тому нам не потрібно вивчати новий інструмент. Фактичний код тесту на інтеграцію - це те, що ви б написали для Вашого Варіанту 1.