Як отримати початковий API правильно за допомогою TDD?


12

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

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

Коли ви вдосконалюєте існуючий код, TDD здається справді гарним, але коли ви пишете все з нуля, API, для якого ви пишете тести, трохи "розмито", якщо ви не зробите перед собою великий дизайн. Що ви робите, коли у вас вже є 30 тестів на метод, який змінив його підпис (і в цій частині, поведінку)? Це багато тестів, які потрібно змінити, коли вони складатимуться.


3
30 тестів за одним методом? Здається, що цей метод має занадто складну ситуацію, або ви пишете занадто багато тестів.
Мінтос

Ну, можливо, я трохи перебільшив, щоб висловити свою думку. Після перевірки коду, як правило, у мене є менше 10 методів на тест, більшість з яких нижче 5. Але все, що стосується "повернення назад та зміни їх рукою", було дуже неприємним.
Вітовт Макконіс

6
@Minthos: Я можу подумати про 6 тестів у верхній частині голови, що будь-який метод, що бере строку, часто не спрацьовує або погано спрацьовує під час запису першої чернетки (нульовий, порожній, занадто довгий, неправильно локалізований, неправильне масштабування перф.) . Аналогічно для методів, які беруть колекцію. Для нетривіального методу 30 звучить велико, але не надто нереально.
Стівен Еверс

Відповіді:


13

Те, що ви називаєте "великим дизайном наперед", я називаю "розумним плануванням архітектури вашого класу".

Ви не можете виростити архітектуру з одиничних тестів. Це говорить навіть дядько Боб .

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

http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf , Сторінка 4

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

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


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

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

2
+1, і це не суперечить TDD. TDD починається, коли ви починаєте писати код, саме тоді, коли закінчується дизайн. TDD може допомогти вам побачити, що ви пропустили в дизайні, дозволяючи переробляти дизайн та реалізацію та продовжувати.
Стівен Еверс

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

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

3

Якщо ви робите TDD. Ви не можете змінити підпис і поведінку, не керуючи ними тестами. Таким чином, 30 тестів, які вийшли з ладу, були або видалені в процесі, або змінені / відновлені разом з кодом. Або вони тепер застарілі, безпечні для видалення.

Ви не можете ігнорувати 30 разів червоного у циклі червоно-зеленого рефактора?

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

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

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


1

Як тільки що сказав Роберт Харві, ви, мабуть, намагаєтесь використовувати TDD для чогось, що має оброблятися іншим концептуальним інструментом (тобто: "дизайн" чи "моделювання").

Спробуйте спроектувати (або "моделювати") вашу систему досить абстрактним ("загальним", "невиразним") способом. Наприклад, якщо вам доведеться моделювати автомобіль, достатньо мати клас автомобілів з певним невиразним методом і полем, наприклад, startEngine () та int. Тобто: опишіть те, що ви хочете викрити перед громадськістю , а не як ви хочете це здійснити. Спробуйте розкрити лише основні функціональні можливості (читати, записувати, запускати, зупиняти і т. Д.) І залишити код клієнта докладно розроблений на ньому (prepMyScene (), killTheEnemy () тощо).

Запишіть свої тести, припускаючи цей простий загальнодоступний інтерфейс.

Змінюйте внутрішню поведінку своїх класів і методів, коли вам це потрібно.

Якщо вам потрібно змінити публічний інтерфейс і тестовий набір, зупиніться і подумайте. Швидше за все, це знак того, що у вашому API та в дизайні / моделюванні щось не так.

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

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


0

Я дивлюся на це з точки зору користувача. Наприклад, якщо ваші API дозволяють мені створити об'єкт Person з ім'ям та віком, краще мати конструктор Person (ім'я рядка, int age) та методи аксесуара для імені та віку. Створювати тестові приклади для нових осіб з іменем та віком і віком просто.

дуг

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