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


50

Я намагаюся обернути голову навколо TDD, зокрема частини розробки. Я переглянув деякі книги, але ті, з яких я знайшов, в основному стосуються тестової частини - історії NUnit, чому тестування хороше, Red / Green / Refactor та як створити калькулятор струн.

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

Для ілюстрації уявіть собі ці 3 вимоги:

  • У каталозі має бути список товарів
  • У каталозі слід пам’ятати, які продукти переглядав користувач
  • Користувачі повинні мати можливість шукати товар

У цей момент багато книг витягують чарівного кролика з шапки і просто занурюються в «Тестування ProductService», але вони не пояснюють, як вони дійшли висновку, що в першу чергу існує ProductService. Ось та частина "розвитку" в TDD, яку я намагаюся зрозуміти.

Потрібно мати існуючий дизайн, але речі, що не входять до організацій-служб (тобто: Продукт, тому має бути Служба продуктів), ніде не знайдено (наприклад, друга вимога вимагає від мене певного поняття Користувач, але де я міг би нагадати цю функціональність? І чи пошук є функцією ProductService або окремої SearchService? Як я можу знати, що мені вибрати?)

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

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


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

3
@gnat: Це запит, чому книги TDD не роблять процес проектування зрозумілішим.
Роберт Харві

4
@gnat: Це була твоя редакція, а не моя. :) Дивіться мою зміну назви питання та тіла.
Роберт Харві

9
Якщо ви прочитали твір Роберта К. Мартіна або, можливо, переглянули одне з його відео, ви побачите, що він часто має на увазі дизайн, але він не одружений на ньому. Він вважає, що його заздалегідь задумане уявлення про правильну конструкцію виникне з його тестів, але він цього не змушує. І врешті-решт, інколи такий дизайн робить, а іноді - ні. Моя думка тут, що ваш власний попередній досвід буде керувати вас, але тести повинні вас керувати. Тести повинні мати можливість розробляти або розкривати ваш дизайн.
Ентоні Пеграм

3
Тож це насправді не тестування, а дизайн. Тільки це не дуже допомагає тобі з дизайном, настільки, як допомагає тобі підтвердити дизайн. Але це не так! @ # $ Ing тестування?
Ерік Реппен

Відповіді:


17

Ідея TDD - почати з тестування та працювати над цим. Таким чином, для прикладу "Каталог повинен мати перелік товарів" можна вважати тестом "Перевірка продуктів у каталозі", і, таким чином, це перший тест. Тепер, що містить каталог? Що містить продукт? Це наступні шматки, і ідея полягає в тому, щоб зібрати кілька шматочків і шматочків, які були б на зразок ProductService, який народиться від того, щоб пройти перший тест.

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


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


Основна перевага TDD полягає в тому, що, починаючи з тестів, ви спочатку не замикаєтесь на дизайні. Таким чином, ідея полягає у створенні тестів та створенні коду, який передасть ці тести як методології розробки. Big Design Up Front може викликати проблеми , так як це дає уявлення про фіксацію речі на місці , що робить створювану систему менш спритним в кінці кінців.


Роберт Харві додав це у коментарях, про які варто сказати у відповіді:

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


31
@MichaelStum: На жаль, я думаю, що це поширена помилка щодо TDD: ви не можете розробити архітектуру програмного забезпечення, просто написавши одиничні тести і змусивши їх пройти. Писання одиничних тестів впливає на дизайн, але це не створює дизайн. Ви повинні це зробити.
Роберт Харві

4
@RobertHarvey, JimmyHoffa: якби я міг проголосувати за твої коментарі 100 разів, я би!
Док Браун

9
@ Роберт Харві: Я радий, що ти писав про цю поширену помилку: я занадто часто чую, що потрібно просто сісти і написати всі типи одиничних тестів, і дизайн просто "з'явиться" спонтанно. І якщо ваш дизайн поганий, це тому, що ви не написали достатньо одиничних тестів. Я погоджуюся з вами, що тести - це інструмент для визначення та перевірки вимог до вашого дизайну, але "ви повинні зробити" дизайн самостійно. Я цілком погоджуюся.
Джорджіо

4
@Giorgo, RobertHarvey: +1000 також від RobertHarvey від мене. На жаль, таке хибне уявлення досить поширене, що деякі "експертні" практикуючі TDD / Agile вважають це правдою. Як, наприклад, вони роблять вигляд, що ви можете "еволюціонувати" на вирішенні судоку з TDD, не маючи доменних знань чи аналізу . Цікаво, чи Рон Джеффріс коли-небудь публікував подальші заходи щодо обмежень TDD або пояснив, чому він раптом припинив свій експеримент без жодних висновків чи уроків.
Андрес Ф.

3
@Andres F: Я знаю історію про судоку, і думаю, що це дуже цікаво. Я думаю, що деякі розробники помиляються, думаючи, що інструмент (наприклад, TDD або SCRUM) може замінити знання про домен та власні зусилля, і розраховують, що при механічному застосуванні певного методу гарне програмне забезпечення магічно "з'явиться". Вони часто є людьми, які не люблять витрачати занадто багато часу на аналіз та дизайн та вважають за краще щось кодувати безпосередньо. Для них дотримання певної методології - це алібі для того, щоб не робити належного дизайну. Але це ІМХО - неправильне використання TDD.
Джорджіо

8

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

Як це робиться?

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

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

Це дійсно так просто. Це не чарівний інструмент дизайну.


6

Я намагаюся обернути голову навколо TDD ... Для ілюстрації уявіть собі ці 3 вимоги:

  • У каталозі має бути список товарів
  • У каталозі слід пам’ятати, які продукти переглядав користувач

Ці вимоги слід переробити в людському відношенні. Хто хоче знати, які продукти раніше переглядав користувач? Користувач? Продавець?

  • Користувачі повинні мати можливість шукати товар

Як? По імені? За маркою? Перший крок у розробці тестових програм - це визначення тесту, наприклад:

browse to http://ourcompany.com
enter "cookie" in the product search box
page should show "chocolate-chip cookies" and "oatmeal cookies"

>

У цей момент багато книг витягують чарівного кролика з шапки і просто занурюються в «Тестування ProductService», але вони не пояснюють, як вони дійшли висновку, що в першу чергу існує ProductService.

Якщо це єдині вимоги, я, звичайно, не стрибну, щоб створити ProductService. Я можу створити дуже просту веб-сторінку зі статичним списком продуктів. Це спрацювало б ідеально, поки ви не досягнете вимог додавати та видаляти продукти. У цей момент я можу вирішити, що найпростіше використовувати реляційну базу даних та ORM та створити клас продукту, зіставлений в єдину таблицю. Поки що немає ProductService. Такі класи, як ProductService, будуть створені, коли і якщо вони знадобляться. Можливо, є кілька веб-запитів, які потребують виконання однакових запитів чи оновлень. Тоді буде створений клас ProductService для запобігання дублювання коду.

Підсумовуючи, TDD визначає код, який потрібно записати. Дизайн відбувається, коли ви приймаєте рішення про реалізацію, а потім перефактуруєте код на класи, щоб уникнути дублювання та контролю залежностей. Додаючи код, вам потрібно буде створити нові класи, щоб зберегти код SOLID. Але вам не потрібно заздалегідь вирішувати, що вам знадобиться клас продукту та клас ProductService. Можливо, ви виявите, що життя просто чудове за допомогою лише класу «Продукт».


Гаразд, ні ProductServiceтоді. Але як TDD сказав вам, що вам потрібна база даних та ORM?
Роберт Харві

4
@Robert: так не було. Це дизайнерське рішення, засноване на моєму судження про найбільш ефективний спосіб задоволення вимоги. Але рішення могло змінитися.
кевін клайн

1
Хороший дизайн ніколи не буде створюватися як побічний ефект якогось довільного процесу. Наявність системи або моделі для роботи та кадрування речей - чудово, але тест-TDD, IMO, потрапляє в конфлікт інтересів, продаючи себе як щось, що гарантує, що люди не будуть несподівано покусані побічними ефектами поганого код, який не повинен був відбуватися в першу чергу. Дизайн вимагає роздумів, обізнаності та продуманості. Ти не навчишся їх обрізати автоматично виявлені симптоми з дерева. Ви дізнаєтесь їх, з’ясувавши, як у першу чергу уникнути злих мутантних гілок.
Ерік Реппен

Я думаю, що тест "додає продукт; перезавантажте комп'ютер і перезавантажте систему; доданий продукт повинен бути видимим. " показує, звідки виникає потреба в якійсь базі даних (але це все ще може бути плоский файл або XML).
yatima2975

3

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

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

  • Візьміть купу підрядників на спецробота і скажіть їм, що їх команда Agile і Test First. Вони часто не мають звички, крім того, щоб працювати спец, і вони не турбуються про якість роботи, якщо це триває досить довго, щоб закінчити проект.

  • Спробуйте спочатку зробити щось нове випробування, витрачайте значну частину часу на видобування тестів, оскільки ви знайдете різні підходи, а інтерфейси - це лайно.

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

  • Будь-яка ситуація, коли вам не вистачає базової механіки заздалегідь, щоб додати тестову функцію, не записуючи спочатку купу базових нестабільних бітів, як дискова підсистема або інтерфейс зв'язку tcpip.

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

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


1

Я переконаний, що TDD - це дуже цінний підхід до детального проектування системи - тобто API та об'єктної моделі. Однак, щоб дійти до суті в проекті, де ви б почали використовувати TDD, вам потрібно мати велику картину дизайну, яка вже моделюється якось, і вам потрібно мати велику картину архітектури, яка вже моделюється якось. @ user414076 перефразовує Роберта Мартіна як те, що має на увазі дизайнерську ідею, але не одружений на цьому. Саме так. Висновок - TDD - не єдиний проектний напрям, який відбувається, це деталізація дизайну. TDD повинні передувати іншим дизайнерським заходам і підходити до загального підходу (наприклад, Agile), який стосується того, як загальний дизайн створюється та розвивається.

FYI - дві книги, які я рекомендую на тему, що дають відчутні та реалістичні приклади:

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

тестова розробка Практичний посібник - повільна і поетапна прогулянка через розробку повного, хоч і невеликого, додатка.


0

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

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

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


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

1
Правильно, архітектура високого рівня, як модель MVC, не вийде тільки з TDD. Але те, що може виникнути з TDD, - це код, призначений для легкої перевірки, що є дизайном.
Даніель Перейра

0

У питанні зазначено:

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

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


0

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

Що стосується дизайну, я маю на увазі книги Роберта К. Мартіна (Agile Development), але також і моделі Мартіна Фаулера для архітектури корпоративних додатків та дизайну драйверів домену. Особливо пізніше дуже систематично витягує Суб'єкти та відносини з вимог.

Потім, коли ви добре відчуєте доступні вам варіанти щодо управління цими сутностями, ви зможете подати вам TDD-підхід.


0

Чи не призначений TDD спочатку змусити мене відкрити свій дизайн?

Немає.

Як можна випробувати те, що ви не сконструювали спочатку?

Для ілюстрації уявіть собі ці 3 вимоги:

  • У каталозі має бути список товарів
  • У каталозі слід пам’ятати, які продукти переглядав користувач
  • Користувачі повинні мати можливість шукати товар

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

Вам потрібно знати, які інваріанти вашої системи.

Вимога буде щось на зразок:

  • Замовник може замовити товар певної кількості, якщо цього товару є в наявності.

Отже, якщо це єдина вимога, у вас може бути клас на кшталт:

public class Product {

  private int quantity;

  public Product(int initialQuantity) {
    this.quantity = initialQuantity;
  }

  public void order(int quantity) {
    // To be implemented.
  }

}

Потім, використовуючи TDD, ви будете писати тестовий випадок, перш ніж застосовувати метод order ().

public void ProductTest() {

    public void testCorrectOrder() {

        Product p = new Product(10);
        p.order(3);
        p.order(4);

    }

    @Expect(ProductOutOfStockException)
    public void testIncorrectOrder() {

        Product p = new Product(10);
        p.order(7);
        p.order(4);

    }

}

Тож другий тест не вдасться, тоді ви можете реалізувати метод order () так, як вам подобається.


0

Ви цілком правильні, TDD призведе до хорошої реалізації заданої конструкції. Це не допоможе вашому дизайнерському процесу.


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

-3

TDD дуже допомагає, проте є важлива частина в розробці програмного забезпечення. Розробник повинен слухати код , який пишеться. Рефакторинг - це 3-та частина в циклі TDD. Це головний крок, на якому розробник повинен зосередитись і подумати, перш ніж перейти до наступного червоного тесту. Чи є дублювання? Чи застосовуються принципи SOLID? А як щодо високої згуртованості та малої зв'язку? А як з іменами? Погляньте детальніше на код, який з’являється з тестів, і перевірте, чи є щось, що потрібно змінити, переробити. Питання і код підкажуть вам, як він хоче бути спроектований. Я зазвичай пишу набори з декількох тестів, вивчаю цей список і створюю перший простий дизайн, він не повинен бути "остаточним", зазвичай це не так, оскільки це змінюється при додаванні нових тестів. Ось тут і приходить дизайн.

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