Як перевірити код, який залежить від складних API (наприклад, Amazon S3)?


13

Я борюся з тестуванням методу, який завантажує документи на Amazon S3, але я думаю, що це питання стосується будь-якої нетривіальної API / зовнішньої залежності. Я придумав лише три можливі рішення, але жодне не здається задовільним:

  1. Запустіть код, фактично завантажте документ, перевірте в API AWS, що він завантажений, і видаліть його в кінці тесту. Це зробить тест дуже повільним, коштуватиме гроші щоразу, коли тест запускається, і не буде завжди повертати той самий результат.

  2. Макет S3. Це супер волохато, тому що я не маю уявлення про внутрішність цього об'єкта, і він почуває себе неправильно, оскільки це занадто складно.

  3. Просто переконайтесь, що MyObject.upload () викликається правильними аргументами і довіряйте, що я правильно використовую об'єкт S3. Це мене турбує, тому що немає ніякого способу точно знати, я правильно використав API S3 лише з тестів.

Я перевірив, як Amazon тестує свій власний SDK, і вони насміхаються над усім. У них є помічник на 200 ліній, який робить глузування. Я не вважаю, що для мене це практично можливо.

Як я це вирішую?


Не відповідь, але на практиці ми використовуємо три підходи, яких ви викрили.
jlhonora

Відповіді:


28

Тут ми повинні розглянути два питання.

Перший полягає в тому, що ви, здається, дивитесь на всі ваші тести з точки зору тестування одиниці. Одиничні тести є надзвичайно цінними, але це не єдині види тестів. Тести насправді можна розділити на кілька різних шарів, від дуже швидких одиничних тестів до менш швидких інтеграційних тестів до ще повільніших тестів прийняття . (Може бути розбито ще більше шарів, як функціональні тести .)

Друга полягає в тому, що ви поєднуєте дзвінки до стороннього коду зі своєю бізнес-логікою, створюючи проблеми з тестуванням і, можливо, роблячи свій код більш крихким.

Тестові одиниці повинні бути швидкими і виконувати їх потрібно часто. Знущання із залежностей допомагає тримати ці тести швидко, але потенційно може ввести дірки в покритті, якщо залежність зміниться, а макет не буде. Ваш код може бути порушений, поки ваші тести все ще зелені. Деякі знущаються бібліотеки будуть попереджати вас про зміну інтерфейсу залежності, інші не можуть.

Інтеграційні тести, з іншого боку, призначені для перевірки взаємодії між компонентами, включаючи сторонні бібліотеки. На цьому рівні тестування не слід використовувати макети, оскільки ми хочемо побачити, як фактично взаємодіють об'єкти. Оскільки ми використовуємо реальні об’єкти, ці тести будуть повільнішими, і ми не будемо виконувати їх майже так часто, як наші тестові одиниці.

Тести прийняття виглядають на ще більш високому рівні, перевіряючи, чи відповідають вимоги до програмного забезпечення. Ці тести працюють проти всієї повної системи, яка буде розгорнута. Ще раз, ніяких глузувань не слід використовувати.

Одне керівництво, яке люди вважають цінним щодо макетів, - це не знущатися над тими, якими ви не володієте . 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.


питання полягає в тому, як ви запускаєте інтеграцію, а точніше тести e2e для API, який ви піддаєте. Ви не можете знущатися з PersistenceService з зрозумілих причин. Або я щось неправильно зрозумів, або додавання іншого шару між API програми та API AWS не дає нічого більше, ніж простіше часу робити одиничні тести
Yerken,

@Yerken Коли я думаю про це, я майже впевнений, що міг би заповнити ще одну довгу відповідь на це питання. Це може бути для вас навіть вартим, оскільки ви можете отримати більше, ніж просто мою відповідь.
cbojar

4

Вам потрібно зробити і те, і інше.

Запуск, завантаження та видалення - це тест на інтеграцію. Він взаємодіє із зовнішньою системою, тому можна очікувати, що вона буде працювати повільно. Це, мабуть, не повинно бути частиною кожної складеної локально, але вона повинна бути частиною побудови CI або побудови за ніч. Це компенсує повільність цих тестів і надає значення автоматичного тестування.

Вам також потрібні одиничні тести, які працюють швидше. Оскільки, як правило, розумно не занадто сильно залежати від зовнішньої системи (щоб ви могли поміняти місцями виконання або переключитись), ви, мабуть, спробуйте написати простий інтерфейс через S3, проти якого ви можете кодувати. Знущайтеся над цим інтерфейсом у unittests, щоб мати швидкі запущені unittests.

Перші тести перевіряють, чи дійсно ваш код працює з S3, другі перевіряють, чи правильно ваш код називає код, який розмовляє з S3.


2

Я б сказав, що це залежить від складності вашого використання API .

  1. Вам обов'язково потрібно зробити хоча б тестування, яке насправді викликає API S3 і підтверджує, що він працював від кінця до кінця.

  2. Вам також обов'язково потрібно зробити додаткове тестування, яке насправді не викликає API, тому ви можете атестувати власне програмне забезпечення адекватно, не посилаючись на API весь час.

Залишається питання: чи потрібно знущатися над API?

І я думаю, що це залежить від того, скільки ти з цим робиш. Якщо ви виконуєте лише одну чи дві прості дії, я не думаю, що вам потрібно брати участь у всіх клопотах макету. Я був би задоволений, просто перевіривши моє використання функцій і зробивши тестування в реальному часі.

Однак, якщо ваше використання його складніше, з різними сценаріями та різними змінними, які можуть вплинути на результати, вам, мабуть, потрібно знущатися над цим, щоб зробити більш ретельне тестування.


1

Додаючи до попередніх відповідей, головне питання - чи (і як) ви хочете знущатися над API S3 для своїх тестів.

Замість того, щоб вручну висміювати окремі відповіді S3, ви можете скористатися деякими дуже складними існуючими глузуючими рамками. Наприклад, moto забезпечує функціональність, дуже схожу на фактичну API S3.

Ви також можете поглянути на LocalStack , структуру, яка поєднує наявні інструменти та забезпечує повністю функціональне локальне хмарне середовище (включаючи S3), що полегшує тестування інтеграції.

Хоча деякі з цих інструментів написані іншими мовами (Python), слід тестувати середовище тесту у зовнішньому процесі, наприклад, у Java / JUnit.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.