Розробка, орієнтована на тестування: уривок із книжки Роберта Мартіна

  • 27 января, 11:23
  • 4315
  • 0

У видавництві «Фабула» вийшла друком книга «Чистий кодер» Роберта Мартіна в українському перекладі. Роберт ,також відомий як Дядечко Боб й один із творців всесвітньовідомого Agile-маніфесту, написав книжку для всіх, хто займається програмуванням і прагне бути справжнім професіоналом, незалежно від досвіду.  У ній Роберт викладає свої очікування від професійного розробника у всіх можливих аспектах: із погляду управлінських взаємодій, тайм-менеджменту, зовнішнього тиску, співпраці в команді та вибору відповідних інструментів. 

Розробка, орієнтована на тестування

 Минуло понад десять років відтоді, як методологія розробки через тестування (TDD, Test Driven Development) з’явилася в нашій галузі. Спочатку вона застосовувалася на хвилі екстремального програмування (XP, eXtreme Programming), але з того часу була використана Scrum і майже всіма іншими гнучкими (Agile) методологіями. Навіть команди, які не користуються гнучкими методологіями, застосовують TDD.  

Коли 1998 року я вперше почув про «випереджувальне тестування» («Test First Programming»), то поставився до нього скептично. Та й хто би тоді вчинив інакше? Як це —  починати роботу з написання модульних тестів? Навіщо робити такі дурниці? Але на той час я вже мав тридцятирічний досвід професійного програмування; я бачив, як у галузі з’являються та зникають численні нові ідеї, і чудово розумів, що нічого не варто заперечувати, особливо якщо це рекомендує така людина, як Кент Бек.

Отже, у 1999 році я вирушив до Медфорда (штат Орегон), аби зустрітися з Кентом і навчитися в нього нової методології. Результат виявився просто вражаючим!

Ми з Кентом засіли в його офісі і почали програмувати просте завдання на Java. Я хотів написати якийсь примітивний код, але Кент заперечив і провів мене по всьому процесу крок за кроком. Спочатку він написав крихітну частку модульного тесту, яку і кодом не можна було назвати. Потім написав код, достатній для того, щоби компілювався тест. А потім він написав ще один тест і ще трохи коду.

Подібний робочий цикл цілком суперечив усьому моєму досвіду. Я звик писати код не менше години, перш ніж намагатися відкомпілювати або запустити його. Але Кент виконував свій код буквально кожні 30 секунд або близько того. Це виглядало неймовірно!

Але найцікавішим було те, що цей робочий цикл був мені відомий! Я стикався з ним багато років тому, коли ще дитиною[1] програмував ігри інтерпретованими мовами на зразок Basic або Logo. У цих мовах не було складання як такого: ви просто додавали рядок коду й запускали програму. Робочий цикл відбувався дуже швидко. І тому програмування цими мовами було дуже продуктивним.

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

Однак переді мною сидів Кент, який писав свою програму на Java із тридцятисекундними циклами — і без найменшого натяку на те, що робота вповільниться. І тоді до мене дійшло, що ця проста методологія дозволяє програмувати справжніми мовами з тривалістю робочого циклу, типовою для Logo!

І я капітально «підсів» на неї!

Вердикт винесено

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

Але спочатку я маю повідомити наступне:

  1. Вердикт винесений!
  2. Дебати завершені.
  3. Команда GOTO шкідлива.
  4. А TDD — працює.

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

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

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

Утім, останню пропозицію варто розглянути докладніше. Що це, власне, таке TDD?

Три закони TDD

1. Новий робочий код пишеться тільки після написання невдалого модульного тесту.

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

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

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

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

Молитва про вигоди

 Упевненість

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

Я є основним автором і відповідальним за супровід FitNesse[2] — системи приймального тестування на базі Java. На момент написання цієї книги код FitNesse складався з 64 000 рядків, із яких 28 000 містилися у 2200 окремих модульних тестах. Ці тести забезпечують покриття щонайменше 90 % робочого коду[3], а їхнє виконання триває близько 90 секунд.

Щоразу, коли я змінюю якусь частину FitNesse, то запускаю модульні тести. Якщо вони проходять успішно, я отримую практично повну впевненість, що зміни нічого не порушили. Наскільки повну? У будь-якому випадку достатню, щоб опублікувати оновлену версію!

Весь процес контролю якості FitNesse зводиться до команди ant release. Ця команда збирає FitNesse «з нуля», а потім запускає всі модульні та приймальні тести. Якщо всі тести проходять успішно, я публікую результат.

Зниження щільності дефектів

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

І цей ефект не є унікальним. Істотне зниження кількості дефектів при використанні TDD описане в цілій низці звітів[4] та досліджень[5]. Від IBM до Microsoft, від Sabre до Symantec — компанія за компанією і команда за командою повідомляють про зниження кількості дефектів у 2, 5 і навіть 10 разів. Справжній професіонал не може ігнорувати такі показники.

Сміливість

 Чому ми не виправляємо невдалий код одразу ж, як бачимо його? Зазвичай наша перша реакція на безладну функцію: «Ну й мішанина, слід усе виправити». Друга реакція: «Нехай це зробить хтось інший!» Чому? Тому що ви знаєте, що, ледве торкнувшись коду, ви ризикуєте його «зламати»; а якщо код буде «зламаний», то відповідальність за нього  автоматично переходить до вас.

А якщо ви будете впевнені, що впорядкування коду нічого не порушить? Що ви матимете таку впевненість, про яку я щойно згадав? Якщо ви просто натискаєте кнопку і за 90 секунд дізнаєтеся, що зміни нічого не порушили, а принесли користь?

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

Коли розробник позбавляється страху перед чисткою коду, він його чистить! Чистий код легше зрозуміти, простіше змінювати та легше розширювати. Зі спрощенням коду ймовірність дефектів стає ще нижчою. Відбувається стабільне поліпшення кодової бази — замість «загнивання коду», звичного для нашій галузі.

Хіба професійний розробник може дозволити, щоби «загнивання» тривало?

Документація

 Чи доводилося вам колись застосовувати сторонній фреймворк? Фірма-розробник зазвичай надсилає красиво оформлену інструкцію, створену технічними письменниками. Типова версія містить 27 глянцевих ілюстрацій із кружечками та стрілочками; на звороті кожної ілюстрації присутній абзац тексту з описом налаштування, розгортання та інших операцій із цією структурою. А наприкінці, десь у додатку, ховається маленький кривенький розділ із прикладами коду.

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

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

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

Архітектура

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

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

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

Таким чином, дотримання трьох законів та випереджаюче створення тестів сприяють більш якісній архітектурі з меншою кількістю прив’язок. А хіба професіонал відмовиться від інструментів, чиє застосування призводить до вдосконалення архітектури?

«Але ж я можу написати тести пізніше!» — заперечите ви. Ні, не можете. Звісно, деякі тести дійсно можна написати пізніше. Можна навіть наблизитися до високого покриття, якщо ви ретельно його виміряєте. Проте тести, написані пізніше, лише захищають від помилок. А тести, написані з випередженням, активно атакують їх. Тести, створені пізніше, пишуться розробником, який сформував код і знає, як вирішувалося завдання. Такі тести жодним чином не можна порівнювати за повнотою та актуальністю з тестами, написаними заздалегідь.

Вибір професіоналів

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

Чим TDD не є

З усіма своїми перевагами TDD — не релігія і не панацея. Дотримання умов трьох законів не гарантує жодної з перерахованих переваг. Поганий код можна написати навіть при попередньому написанні тестів. Та й самі тести теж можуть виявитися написаними невдало.

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


0 комментариев
Сортировка:
Добавить комментарий

IT Новости

Смотреть все