З точки зору TDD, чи я погана людина, якщо тестую проти живої кінцевої точки замість макети?


16

Я дотримуюся TDD релігійно. Мої проекти, як правило, мають 85% або краще тестового покриття, із значущими тестовими кейсами.

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

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

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

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

Що ви все думаєте?


8
Жива кінцева точка насправді не є одиничним тестом, чи не так? Це інтеграційний тест. Але, зрештою, це, мабуть, питання прагматизму; ви можете або витрачати час на написання макетів, або витратити час на написання функцій або виправлення помилок.
Роберт Харві

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

14
Ти не погана людина. Ти хороша людина, що робить погану справу.
Kyralessa

15
Я дотримуюся TDD релігійно Можливо, це проблема? Я не думаю , що будь - яка з цієї методології покликане бути прийнято , що серйозно. ;)
FrustratedWithFormsDesigner

9
Дотримуватися TDD релігійно означає, що ви відкинете 15% непокритий код.
mouviciel

Відповіді:


23
  • Першою моєю рекомендацією було б не знущатися над типом, яким ви не володієте . Ви згадали, що HTable є справжнім болем для знущань - можливо, вам слід замінити його в адаптер, який демонструє 20% необхідних функцій HTable, і знущатися над обгорткою там, де потрібно.

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

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

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

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


+1 Мені здається, що набагато простіше будувати крайові випадки за допомогою макетних тестів, ніж заповнювати базу даних цими випадками.
Роб

Я згоден з більшістю вашої відповіді. Однак я не впевнений, що згоден з частиною адаптера. HTable - це глузування з болю, тому що це досить голий метал. Наприклад, якщо ви хочете виконати операцію з отримання пакету, вам потрібно створити купу об’єктів Get, помістити їх у список, а потім викликати HTable.batch (). З глузливої ​​точки зору це серйозна біль, тому що ви повинні створити спеціальний Matcher, який вивчає список об’єктів, які ви передаєте в HTable.batch (), а потім повертає правильні результати для цього списку об’єктів get (). СЕРІЙНИЙ біль.
sangfroid

Я гадаю, що я міг би створити хороший, привітний клас обгортки для HTable, який би опікувався всім веденням господарства, але в цей момент ... я відчуваю, що я начебто будую рамки навколо HTable, і чи справді це моє завдання? Зазвичай "Давайте побудуємо рамки!" це знак, що я йду в неправильному напрямку. Я міг би проводити дні, пишучи заняття, щоб зробити HBase більш доброзичливим, і не знаю, чи це чудово використовує мій час. Плюс, тоді я розміщую інтерфейс або обгортку замість простого старого об’єкта HTable, і це, безумовно, зробить мій код більш складним.
sangfroid

Але я згоден з вашим головним моментом, що не слід писати макети для класів, якими вони не володіють. І, безумовно, погоджуйтесь, що немає сенсу писати макетний тест, який перевіряє те саме, що і тест на інтеграцію. Здавалося б, макети найкращі для тестування інтерфейсів / контрактів. Дякую за пораду - це мені дуже допомогло!
sangfroid

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

11

По-філософськи, тести, які використовують макети, повинні мати пріоритет над тестами, які використовують живу кінцеву точку

Я думаю, щонайменше, це точка поточної суперечки серед прихильників TDD.

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

Основним винятком є ​​те, що ви використовуєте дуже загальний інтерфейс, можливо, на основі анотацій чи рефлексій, що компілятор не може автоматично корисно поліціювати. Навіть тоді вам слід перевірити, чи існує спосіб проведення перевірки програмно (еквівалентна бібліотека перевірки синтаксису SQL), а не вручну, використовуючи макети.

Це останній випадок, який ви робите, коли ви протестуєте з "живою" локальною базою даних; реалізація Htable стартує і застосовує набагато більш повне підтвердження міжфакторного договору, ніж ви коли-небудь думали написати вручну.

На жаль, набагато більш поширеним використанням макетного тестування є тест, який:

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

Такі випробування, звичайно, слід видалити з поля зору.


1
Я не можу підкріпити це досить. Я вважаю за краще покриття 1шт із великими тестами, ніж покриття наповнювачем 100шт.
Ян

3
Тести, засновані на макетах, справді описують контракт, який використовують два об'єкти для спільного спілкування, але вони виходять далеко за рамки системи типів такої мови, як Java. Справа не лише в підписах методів, вони також можуть вказувати дійсні діапазони значень аргументів або повернених результатів, які винятки є прийнятними, в якому порядку і скільки разів можна викликати методи тощо. Сам компілятор не попередить вас, якщо є є зміни в тих. У цьому сенсі я не думаю, що вони взагалі зайві. Дивіться infoq.com/presentations/integration-tests-scam для отримання додаткової інформації про макетні тести.
guillaume31

1
... згоден, тобто тестування логіки навколо виклику інтерфейсу
Роб

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

"їх специфікація неявна": ні, якщо ви пишете контрактні тести для своїх інтерфейсів ( blog.thecodewhisperer.com/2011/07/07/contract-tests-an-example ) та дотримуєтесь їх під час налаштування макетів.
guillaume31

5

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


4

Я повністю погоджуюся з відповіддю guillaume31, ніколи не знущайтесь над тими, якими ви не володієте !.

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

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

Ознайомтеся з цією формою розмови Іана Купера: http://vimeo.com/68375232 , він розповідає про шестикутну архітектуру та тестування, він розповідає про те, коли і над чим знущатися, по-справжньому натхненну розмову, яка вирішує багато питань, як ваша, щодо справжнього TDD .


1

TL; DR - Як я це бачу, це залежить від того, скільки зусиль ви закінчите витрачати на тести, і чи було б краще витратити більше цього на вашу фактичну систему.

Довга версія:

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

Наприклад, деякі основні значення, які я отримую від тестів:

  • Надійність (і, отже, швидкість розвитку): код рефактора / інтегруйте новий фреймворк / замініть компонент / порт на іншу платформу, будьте впевнені, що все ще працює
  • Відгуки про дизайн: класичний TDD / BDD "використовувати свій код" зворотній зв'язок щодо ваших інтерфейсів низького / середнього рівня

Тестування на живій кінцевій точці все ж має забезпечувати це.

Деякі недоліки тестування на живу кінцеву точку:

  • Налаштування навколишнього середовища - налаштування та стандартизація тестового середовища роботи - це більше роботи, а тонко різні настройки середовища можуть спричинити тонко різну поведінку
  • Безгромадянство - робота проти живої кінцевої точки може закінчитися просуванням тестів на письмовій формі, які покладаються на мутуючий стан кінцевої точки, який є крихким і важко аргументувати (тобто коли щось не вдається, чи не виходить із-за дивного стану?)
  • Тестове середовище запуску тендітне - якщо тест не вдається, це тест, код або реальна кінцева точка?
  • Швидкість бігу - жива кінцева точка зазвичай повільніше, а іноді складніше паралелізувати
  • Створення крайових випадків для тестування - як правило, банальне з макетом, іноді біль із живою кінцевою точкою (наприклад, хитрі налаштування - помилки транспорту / HTTP)

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


1

З точки зору тестування, існують деякі вимоги, які є абсолютно необхідними:

  • Тестування (одиничне чи інше) ніколи не повинно впливати на виробничі дані
  • Результати одного тесту ніколи не повинні впливати на результати іншого тесту
  • Завжди потрібно починати з відомої позиції

Це велике завдання при підключенні до будь-якого джерела, який підтримує стан поза вашими тестами. Це не "чистий" TDD, але екіпаж Ruby on Rails вирішив цю проблему таким чином, щоб він міг бути адаптований до ваших цілей. Рамка для тестування рейок працювала таким чином:

  • Конфігурація тесту була автоматично обрана під час запуску тестів одиниць
  • База даних була створена та ініціалізована на початку запуску тестових одиниць
  • База даних була видалена після запуску одиничних тестів
  • Якщо використовується SqlLite, тестова конфігурація використовувала базу даних RAM

Вся ця робота була вбудована в тестовий джгут, і вона працює досить добре. Тут є набагато більше, але основ для цього є досить.

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

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

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