Я ходив по колах, намагаючись зрозуміти найкращий спосіб перевірити клієнтську бібліотеку API, яку я розробляю. Бібліотека має Client
клас, який в основному має зіставлення 1: 1 з API, та додатковий Wrapper
клас, який забезпечує більш зручний інтерфейс у верхній частині Client
.
Wrapper --> Client --> External API
Я спершу написав купу тестів проти обох Client
і Wrapper
, фактично просто перевіривши, що вони пересилають на відповідні функції будь-якої операції ( Wrapper
працює Client
і Client
працює на HTTP-з’єднанні). Однак я почав відчувати себе незручно, тому що відчуваю, що я тестую реалізацію цих класів, а не інтерфейс. Теоретично я міг би змінити класи, щоб мати іншу ідеально реалізовану реалізацію, але мої тести не зможуть, оскільки функції, які я очікував, будуть викликані, не викликаються. Це звучить як тендітне тестування для мене.
Після цього я подумав про інтерфейс класів. Тести повинні підтвердити, що класи дійсно виконують роботу, яку вони мають намір робити, а не як вони це роблять. То як я можу це зробити? Перше, що спадає на думку, це заглушити зовнішні запити API. Однак я нервую надмірно спрощуючи зовнішню службу. Я бачив безліч прикладів нарізаних API, які дають лише консервовані відповіді, що звучить як дійсно простий спосіб лише перевірити, чи правильно ваш код працює проти вашої підробленої API. Альтернативою є знущання над сервісом, який просто нездійсненний, і його потрібно буде постійно оновлювати, коли зміниться реальна послуга - це відчуває себе як надмір і втрата часу.
Нарешті, я прочитав це з іншої відповіді на програмістів SE :
Завдання віддаленого клієнта API - видавати певні дзвінки - ні більше, ні менше. Тому його тест повинен підтвердити, що він видає ці дзвінки - ні більше, ні менше.
І тепер я більш-менш переконаний - під час тестування Client
все, що мені потрібно перевірити, - це те, що він робить правильні запити до API (звичайно, завжди є можливість, що API зміниться, але мої тести продовжують проходити - але це де тести на інтеграцію були б корисними). Оскільки Client
це лише 1: 1 зіставлення з API, я не переймаюся переходом від однієї дійсної реалізації до іншої - не існує жодної дійсної реалізації для кожного методу Client
.
Однак я все-таки застряг із Wrapper
класом. Я бачу такі варіанти:
Я замовчую
Client
клас і просто перевіряю, чи називаються відповідні методи. Таким чином я роблю те саме, що вище, але трактуюClient
API як резервний інтерфейс для API. Це повертає мене туди, де я почав. Знову ж таки, це дає мені незручне відчуття щодо тестової реалізації, а не інтерфейсу. ЦеWrapper
дуже вдало реалізувати за допомогою зовсім іншого клієнта.Я створюю макет
Client
. Тепер я маю вирішити, як далеко пройти з його глузуванням - створення повноцінного знущання над сервісом зайняло б багато зусиль (більше роботи, ніж пройшло в самій бібліотеці). API сам по собі простий, але сервіс досить складний (по суті це сховище даних з операціями над цими даними). І знову, мені доведеться тримати свою глузування синхронно з реальноюClient
.Я просто перевіряю, чи робляться відповідні запити HTTP. Це означає, що
Wrapper
буде дзвонити через реальнийClient
об’єкт, щоб зробити ці HTTP запити, тому я насправді не перевіряю його ізольовано. Це робить трохи жахливим одиничне випробування.
Тому я не особливо задоволений жодним із цих рішень. Що б ти зробив? Чи є правильний шлях для цього?