Вступ в Test-Driven Development
Test-Driven Development (TDD) та модульне тестування — це дві суміжні концепції. У цьому розділі розглядається історія цих концепцій.
Історія модульного тестування та Test Driven Development
Модульне тестування (англ. unit testing) — це не нова концепція в розробці програмного забезпечення. Одне з перших згадувань було в статті David J. Panzl «Test procedures: A new approach to software verification» опублікованої 13 жовтня 1976 року на другої конференції з програмної інженерії (2nd International Conference on Software Engineering, ICSE ʼ76). Цитата зі статті:
Тестова процедура — це формальна специфікація тестових випадків, які слід застосувати до одного або кількох цільових модулів програми. Тестові процедури є виконуваними. Процес, що називається ПЕРЕВІРНИКОМ, застосовує тестові процедури до відповідних модулів і створює звіт про винятки, у якому вказано, які тестові випадки, якщо такі є, не пройдено.
Тестова процедура (англ. test procedure) — це в сучасній поширеній термінології модульний тест (unit test), процесс-перевірник (англ. verifier) — це тестовий фреймворк (test framework), звіт про винятки (англ. exception report) — це звіт з тестування (test log, test report), ну а тестові випадки (англ. test cases) так і залишилися тестовими випадками. Хоча в роботі згадується, що тести записувались мовою TPL (Test Procedure Language), та працювали для програм записаних мовою програмування Фортран (Fortran), але тут присутня важлива ознака: «тестові процедури є виконуваними», тобто можна запустити ці тести та отримати звіт з тестування в автоматичному режимі без ручної праці.
У 1987 році була випущена перша версія мови програмування Perl, яка одразу включала модуль Test::Harness
авторів Tim Bunce та Andreas König. Цей модуль допомагав авторам модулів на Perl писати автоматичні тести за допомогою Test Anything Protocol (TAP). TAP — це простий текстовий інтерфейс між модулями тестування в тестовому середовищі. Він відокремлює фіксацію помилок при тестуванні від представлення звітів.
Приклад TAP-звіту виконання 4 тестів:
1..4
ok 1 - Input file opened
not ok 2 - First line of the input valid
ok 3 - Read the rest of the file
not ok 4 - Summarized correctly # TODO Not written yet
У 1989 році Кент Бек (Kent Beck) написав статтю «Simple Smalltalk Testing: With Patterns», де зазначив, що «Smalltalk зазнав втрат через відсутність культури тестування». Ми вважаємо це важливим висловлюванням, тому що тепер модульні тести розглядаються не лише як метод програмування або інструмент, а як культурне явище. Також Бек запропонував фреймворк SUnit для модульного тестування програм мовою програмування Smalltalk. В цьому фреймворку модульні тести та сам модуль були записані однією мовою. Введені такі концепції як Fixture, Test Case, Check, Test Suite. Досвідченим програмістам ці терміни дуже знайомі, вони використовують їх щодня, коли пишуть модульні тести. Тобто методологія модульних тестів концептуально не змінювалась вже майже 40 років.
SUnit та закладені у нього концепції дали поштовх для створення цілої родини тестових фреймворків, які узагальнено називають xUnit, наприклад:
- JUnit — фреймворк модульного тестування для мови програмування Java (Kent Beck, Erich Gamma, 1997 рік);
- NUnit — для усіх мов програмування платформи .Net (2000 рік);
- RUnit — для мови програмування R.
У 2002 році Кент Бек публікує книгу «Test-Driven Development by Example» (Addison-Wesley), в якій були описані принципи Test-Driven Development (TDD), тобто розробки керованою тестами.
Кент Бек каже, що він «перевідкрив» TDD, тобто записав давню концепцію у сучасному контексті програмування:
Оригінальний опис TDD містився в давній книзі про програмування. У ній говорилося, що спочатку ви берете вхідну стрічку, вручну набираєте очікувану вихідну стрічку, а потім програмуєте доти, доки фактична вихідна стрічка не збігатиметься з очікуваною. Після того, як я написав перший фреймворк xUnit на Smalltalk, згадав про це і вирішив спробувати. Так для мене й виникла TDD. Коли я розповідаю про TDD старшим програмістам, часто чую у відповідь: «Авжеж. А як інакше можна програмувати?» Тому я називаю свою роль «повторним відкриттям» TDD.
Схожа концепція згадується у книзі «Digital Computer Programming» (D.D. McCracken, 1957):
Першу атаку на проблему перевірки результатів можна здійснити ще до початку кодування. Щоб повністю переконатися в точності відповідей, необхідно мати контрольний приклад з розрахунками вручну, з яким можна буде порівняти відповіді, що пізніше обчислюватимуться машиною. Це означає, що машини зі збереженою програмою ніколи не використовуються для справжньої одноразової задачі. Завжди має бути елемент ітерації, щоб це було виправдано. Розрахунки вручну можна виконувати на будь-якому етапі програмування. Проте часто компʼютерами керують фахівці з обчислень, які готують задачі як послугу для інженерів чи науковців. У таких випадках вкрай бажано, щоб саме «замовник» підготував контрольний приклад, головним чином тому, що такий підхід дозволяє виявити логічні помилки та непорозуміння між програмістом і замовником. Якщо саме замовник готує тестове рішення, йому краще почати це завчасно до фактичної перевірки, оскільки для будь-якої задачі помірного розміру ручні розрахунки займуть кілька днів або навіть тижнів.
У 2003 році зʼявляються перші повідомлення щодо успішного впровадження методології TDD та позитивному ефекті від впровадження. В компанії IBM досягли скорочення вдвічі кількості помилок в коді в проєкті, що розроблявся за методологією TDD. Інша команда дослідників повідомила про покращення якості продукту на 18%, що був розроблений за методологією TDD. Окрім цього, повідомляється про кращу організацію коду, перевиконання планів з покриття коду тестами та підвищену мотивацію розробників, які застосовували TDD.
У 2007 році Жерард Месарош (Gerard Meszaros) публікує книгу «xUnit Test Patterns. Refactoring Test Code» (Addison-Wesley). В книзі запроваджені такі важливі поняття, як тестопридатність (testability), тестопридатна розробка (design for testability), якість тестів, тестові двійники (test doubles).
Сучасний стан методології TDD
TDD та модульні тести — це дві ключові техніки в загальновідомій Agile-методології розробки програмного забезпечення (практики Extreme Programming та Testing).
Паралельно з TDD набули популярності схожі практики розробки програмного забезпечення:
- Acceptance-Testing-Driven Development (ATDD) фокусується на вимогах бізнесу: перед початком розробки команда (розробники, тестувальники та замовники) спільно формулює тести приймання, які описують бажану поведінку системи з точки зору користувача. Таким чином, TDD орієнтований на технічну якість коду, а ATDD — на відповідність очікуванням замовника.
- Behavior-Driven Development (BDD) розширює ідеї TDD, але робить акцент на поведінці системи з точки зору користувача, використовуючи зрозумілу для всіх сторін мову (наприклад, Given-When-Then). Тоді як TDD більше про правильність реалізації, BDD — про зрозуміле описання очікуваної поведінки системи, що полегшує співпрацю між бізнесом, тестувальниками і розробниками.
Сьогодні TDD та навички модульного тестування — це поширені вимоги до кандидатів на роль розробника програмного забезпечення. Наприклад, українська компанія mono очікує, що кандидат на роль Java Technical Lead пише unit-тести з «із заплющеними очима» 😺. А в українськії компанії Design and Test Lab існує мантра «Ми не пишемо без тестів»!
Посилання
- David J. Panzl. 1976. Test procedures: A new approach to software verification. In Proceedings of the 2nd international conference on Software engineering (ICSE ʼ76). IEEE Computer Society Press, Washington, DC, USA, 477–485.
- Test Anything Protocol
- Kent Beck «Simple Smalltalk Testing: With Patterns» (1989)
- E. M. Maximilien and L. Williams, «Assessing Test-Driven Development at IBM,» in Software Engineering, International Conference on, Portland, Oregon, 2003, pp. 564, doi: 10.1109/ICSE.2003.1201238.
- What is TDD?, Agile Alliance