JavaRush/Java блог/Random UA/Що таке TDD та модульне тестування [переклад]
Dr-John Zoidberg
41 рівень

Що таке TDD та модульне тестування [переклад]

Стаття з групи Random UA
учасників
Ця стаття – адаптація глави книги «Посібник з кар'єри повного програмного забезпечення». Її автор, Джон Сонмез (John Sonmez) пише її та викладає деякі розділи на свій сайт.
Що таке TDD та модульне тестування [переклад] - 1

Короткий глосарій для новачків

Модульне тестування чи юніт-тестування (unit testing) — процес у програмуванні, що дозволяє перевірити на коректність окремі модулі вихідного коду програми. Ідея полягає в тому, щоб писати тести для кожної нетривіальної функції чи методу. Регресоїнне тестування (regression testing) - узагальнена назва для всіх видів тестування програмного забезпечення, спрямованих на виявлення помилок у вже протестованих ділянках вихідного коду. Такі помилки, коли після внесення змін до програми перестає працювати те, що мало продовжувати працювати, називають регресійними помилками. Червоний результат, fail – провал тесту. Відмінність очікуваного результату від фактичного. Зелений результат, pass- Позитивний результат тесту. Фактичний результат не відрізняється від отриманого. ***
Що таке TDD та модульне тестування [переклад] - 2
З підходом «розробка через тестування» (Test Driven Development, TDD) і модульним тестуванням у мене склалися дуже неоднозначні відносини, які плавно переходять від любові до ненависті і назад. Я був пристрасним фанатом і водночас підозрілим скептиком щодо використання цієї та й інших «найкращих практик». Причина мого відношення обґрунтована тим, що в процесах розробки ПЗ намітилася серйозна проблема: розробники, а часом і менеджери, застосовують якийсь інструмент та методологію лише тому, що ті належать до «найкращих практик». Реальна причина їх застосування залишається незрозумілою. Одного разу я приступив до роботи над проектом, і в процесі мені повідомабо, що ми модифікуватимемо код, покритий величезною кількістю модульних тестів. Чи жарт, їх було близько 3000. Зазвичай це хороший знак, сигнал про те, що розробники застосовують передові методології. Код за такого підходу найчастіше структурований, а основі лежить продумана архітектура. Словом, наявність тестів мене втішила вже тому, що це означало полегшення моєї роботи наставника програмістів. Оскільки модульні тести у нас вже були, мені залишалося лише підключити команду розробників до їхньої підтримки та починати писати власний код. Я відкрив IDE (інтегроване середовище розробки) та завантажив проект. мені залишалося лише підключити команду розробників до їхньої підтримки та починати писати власний код. Я відкрив IDE (інтегроване середовище розробки) та завантажив проект. мені залишалося лише підключити команду розробників до їхньої підтримки та починати писати власний код. Я відкрив IDE (інтегроване середовище розробки) та завантажив проект.
Що таке TDD та модульне тестування [переклад] - 3
Це був великий проект! Я знайшов папку з написом "unit tests". «Дуже добре, — подумав я. — Запустимо і побачимо, що станеться. Це зайняло всього кілька хвабон, і, на мій подив, всі тести пройшли, все було зеленим ( "зелений" - позитивний результат роботи тесту. Сигналізує про те, що код працює, як передбачається. Червоним кольором відзначається "провал" або fail, то є той випадок, коли код працює неправильно — прим.). Вони всі пройшли перевірку. У цей момент у мені прокинувся скептик. Як так, три тисячі модульних тестів і всі одразу взяли — і дали позитивний результат? За свою довгу практику я не міг згадати нагоди початку роботи з проектом, щоб у коді не було жодного негативного модульного тесту. Що ж робити? Перевіряти вручну! ЧЯ вибрав один випадковий тест, не найпоказовіший, зате відразу було зрозуміло, що він перевіряє. Але, розбираючись з ним, я помітив деяку нісенітницю: у тесті не було порівнянь з очікуваним результатом (asserts)! Тобто насправді взагалі нічогоне перевірялося! У тесті були деякі кроки, вони виконувались, але наприкінці тесту, де він повинен звіряти фактичний і очікуваний результат, перевірки не було. "Тест" нічого не тестував. Я відкрив ще один тест. Ще краще: оператор порівняння з результатом був закоментований. Блискуче! Це чудовий спосіб зробити пробний прохід, просто закоментуйте код, який змушує його зазнавати невдачі. Я перевірив ще один тест, потім ще один… Жоден із них нічого не перевіряв. Три тисячі тестів, і всі повністю марні. Існує величезна різниця між написанням модульних тестів та розумінням модульного тестування та розробки, заснованої на тестах (TDD).

Що таке модульне тестування?

Що таке TDD та модульне тестування [переклад] - 4
Основна ідея модульного тестування полягає в тому, щоб написати тести, в яких перевірено найменшу «одиницю» коду. Модульні тести зазвичай написані тією ж мовою програмування, як і вихідний код програми. Вони створюються для перевірки цього коду. Тобто модульні тести – це код, який перевіряє коректність іншого коду. Слово "тест" у контексті я використовую досить ліберально, тому що модульні тести в якомусь сенсі тестами не є. Вони нічого не відчувають. Я маю на увазі, що при запуску модульного тесту ви зазвичай не виявляєте, що код не працює. Ви це виявляєте під час написання тесту, оскільки ви змінюватимете код доти, доки тест не стане зеленим. Так, код може змінитися пізніше, і тоді ваш тест може зазнати невдачі. Тож у цьому сенсі модульний тест є регресійним тестом. Модульний тест не схожий на звичайний тест, де ви маєте кілька кроків, які ви збираєтеся виконати, і ви бачите, чи працює програмне забезпечення правильно чи ні. У процесі написання модульного тесту ви виявляєте, чи робить код те, що він повинен чи ні, і змінюватимете код доти, доки тест не буде пройдений.
Що таке TDD та модульне тестування [переклад] - 5
Чому б не написати модульний тест і не перевірити, чи проходить він? Якщо міркувати так, то модульні тести перетворюються на деякі абсолютні вимоги до певних модулів коду на дуже низькому рівні. Ви можете вважати модульний тест абсолютною специфікацією . Модульний тест визначає, що в цих умовах, з цим конкретним набором вхідних даних є результат, який ви повинні отримати від цього модуля коду. Справжнє модульне тестування дозволяє визначити найменшу зв'язкову одиницю коду, яка у більшості мов програмування – принаймні об'єктно-орієнтованих – є класом.

Що іноді називають модульним тестуванням?

Що таке TDD та модульне тестування [переклад] - 6
Часто модульне тестування плутають із інтеграційним тестуванням. Деякі «модульні тести» перевіряють більше одного класу чи тестують великі одиниці коду. Багато розробників стверджують, що вони пишуть модульні тести, хоча насправді пишуть whitebox-тести на низькому рівні. Не варто сперечатися із цими хлопцями. Просто знайте, що вони пишуть інтеграційні тести, а справжні модульні тести ізольовано від інших частин перевіряють найменшу одиницю коду. Ще одна річ, яку часто називають модульним тестуванням - модульні тести без звіряння з очікуваним значенням. Іншими словами, модульні випробування, які насправді нічого не тестують. Будь-який тест, модульний він чи ні, повинен включати якусь перевірку — ми називаємо її звіркою фактичного результату з очікуваним. Саме ця звірка і визначає, проходить тест чи зазнає невдачі. Тест, який завжди проходить, марний. Тест, який завжди зазнає невдачі, марний.

Цінність модульного тестування

Чому я пристрасний прихильник модульного тестування? Чому шкідливо називати «модульним тестуванням» узагальнене тестування, яке включає перевірку не найменшого блоку, ізольованого від іншого коду, а більшого шматка коду? У чому біда, якщо частина моїх тестів не звіряють отримані та очікувані результати? Вони принаймні виконують код. Спробую пояснити.
Що таке TDD та модульне тестування [переклад] - 7
Існує дві основні причини щодо модульного тестування. Перша – покращити дизайн коду. Пам'ятаєте, як я сказав, що модульне тестування це не цілком тестування? Коли ви пишете правильні модульні випробування, ви змушуєте себе ізолювати найменшу одиницю коду. Ці спроби призведуть до того, що ви можете виявити проблеми у структурі самого коду. Вам може бути дуже складно ізолювати клас перевірок і не включати його залежності, і це може змусити вас зрозуміти, що ваш код занадто тісно пов'язаний. Ви можете виявити, що базова функціональність, яку ви намагаєтеся протестувати, поширюється на кілька модулів, що призведе до думки про недостатню когерентність коду. Сідаючи за написання модульного тесту, ви раптово можете виявити (і повірте, так буває!), що ви гадки не маєте, що повинен робити код. Відповідно, ви не зможете написати для нього модульний тест. І звичайно,
Що таке TDD та модульне тестування [переклад] - 8
Якщо під час створення модульних тестів ви суворо дотримуєтеся правила «тестуємо найменшу одиницю коду ізольовано з інших», ви обов'язково виявите всілякі проблеми з цим кодом і дизайном цих модулів. У життєвому циклі розробки програмного забезпечення модульне тестування є скоріше оцінною діяльністю, ніж тестує. Друга основна мета модульного тестування- Створити автоматизований набір регресійних тестів, який може працювати як специфікація поведінки програмного забезпечення на низькому рівні. Що це означає? Коли ви місите тісто, ви його не ламаєте. З цієї точки зору модульні тести – це тести, конкретніше – регресійні тести. Однак мета модульного тестування не в тому, щоб просто будувати регресійні тести. Насправді модульні тести дуже рідко відловлюють регресії, оскільки зміна одиниці коду, який ви тестуєте, майже завжди містить зміни самого модульного тесту. Регресійне тестування набагато ефективніше на вищому рівні, коли тестується код, як «чорна скринька», тому що на цьому рівні внутрішня структура коду може бути змінена, тоді як зовнішня поведінка, як очікується, залишиться незмінною. Модульні тести у свою чергу перевіряють внутрішню структуру, тому коли ця структура змінюється, модульні тести не зазнають невдачі. Вони стають непридатними, і тепер їх потрібно змінити, викинути чи переписати. Тепер ви знаєте більше про справжню мету модульного тестування, ніж багато ветеранів розробки програмного забезпечення.

Що таке технологія через тестування (TDD)?

Що таке TDD та модульне тестування [переклад] - 9
У процесі розробки програмного забезпечення хороша специфікація — на вагу золота. Підхід TDD полягає в тому, що перш ніж написати якийсь код, ви спочатку пишете тест, який служитиме специфікацією, тобто визначати, що повинен робити цей код. Це надзвичайно потужна концепція розробки програмного забезпечення, але найчастіше її неправильно використовують. Зазвичай застосування концепції «розробка через тестування» означає використання модульних тестів управління створенням коду програми. Але насправді цей підхід можна застосовувати будь-якому рівні. Однак у цій статті ми вважатимемо, що застосовуємо модульне тестування для нашої програми. Підхід TDD перевертає все з ніг на голову, і замість того щоб спочатку писати код, а потім писати модульні тести для перевірки цього коду, ви спочатку напишіть модульний тест, а потім напишіть код, щоб цей тест став зеленим. Таким чином, модульне тестування "керує" розробкою коду. Цей процес повторюється знову і знову. Ви пишете ще один тест, який визначає більше функціональності того, що має робити код. Потім ви пишете та модифікуєте код, домагаючись успішного завершення тесту. Після того, як ви отримали «зелений» результат, ви приступаєте до рефакторинг коду, тобто реорганізуєте або очищаєте його, щоб зробити більш коротким. Часто цей ланцюг процесів називають «Червоний-Зелений-Рефакторинг» тому що спочатку модульний тест не проходить (червоний), потім пишеться код, підлаштовуючись під тест, домагаючись, щоб він успішно завершився (зелений), і, нарешті, код оптимізується (рефакторинг) . Цей процес повторюється знову і знову. Ви пишете ще один тест, який визначає більше функціональності того, що має робити код. Потім ви пишете та модифікуєте код, домагаючись успішного завершення тесту. Після того, як ви отримали «зелений» результат, ви приступаєте до рефакторинг коду, тобто реорганізуєте або очищаєте його, щоб зробити більш коротким. Часто цей ланцюг процесів називають «Червоний-Зелений-Рефакторинг» тому що спочатку модульний тест не проходить (червоний), потім пишеться код, підлаштовуючись під тест, домагаючись, щоб він успішно завершився (зелений), і, нарешті, код оптимізується (рефакторинг) . Цей процес повторюється знову і знову. Ви пишете ще один тест, який визначає більше функціональності того, що має робити код. Потім ви пишете та модифікуєте код, домагаючись успішного завершення тесту. Після того, як ви отримали «зелений» результат, ви приступаєте до рефакторинг коду, тобто реорганізуєте або очищаєте його, щоб зробити більш коротким. Часто цей ланцюг процесів називають «Червоний-Зелений-Рефакторинг» тому що спочатку модульний тест не проходить (червоний), потім пишеться код, підлаштовуючись під тест, домагаючись, щоб він успішно завершився (зелений), і, нарешті, код оптимізується (рефакторинг) . як ви отримали «зелений» результат, ви приступаєте до рефакторинг коду, тобто реорганізуєте або очищаєте його, щоб зробити більш коротким. Часто цей ланцюг процесів називають «Червоний-Зелений-Рефакторинг» тому що спочатку модульний тест не проходить (червоний), потім пишеться код, підлаштовуючись під тест, домагаючись, щоб він успішно завершився (зелений), і, нарешті, код оптимізується (рефакторинг) . як ви отримали «зелений» результат, ви приступаєте до рефакторинг коду, тобто реорганізуєте або очищаєте його, щоб зробити більш коротким. Часто цей ланцюг процесів називають «Червоний-Зелений-Рефакторинг» тому що спочатку модульний тест не проходить (червоний), потім пишеться код, підлаштовуючись під тест, домагаючись, щоб він успішно завершився (зелений), і, нарешті, код оптимізується (рефакторинг) .

Що таке TDD?

Що таке TDD та модульне тестування [переклад] - 10
Підхід «розробка через тестування» (TDD), як і модульне тестування може бути використано неправильно. Дуже легко назвати те, що ви робите «TDD», і навіть слідувати практиці, при цьому не розуміючи, чому ви чините саме так. Найбільша цінність TDD у тому, що тести проводять отримання якісних специфікацій. TDD— це, по суті, практика написання точних специфікацій, які можна автоматично перевірити до написання коду. Тести — це найкращі специфікації, тому що вони не брешуть. Вони не скажуть вам після двох тижнів муки з кодом "я мав на увазі зовсім не це". Тести, якщо вони правильно написані, або успішно виконуються, або зазнають невдачі. Тести недвозначно вказують, що саме має відбуватися за певних обставин. Таким чином, ціль TDD — дати нам повне розуміння того, що нам потрібно реалізувати до того моменту, як ми почали реалізовувати. Якщо ви починаєте розробку з TDD, і не можете зрозуміти, що саме тест повинен перевірити, то вам потрібно поставити більше питань. Інша важлива роль TDD полягає у збереженні та оптимізації коду. Підтримка коду – дороге задоволення. Я часто жартую, що найкращий програміст — той, хто напише найкоротший код, який вирішить якесь завдання. Або навіть той, хто доведе, що це завдання вирішувати не потрібно, і тим самим повністю видалить код, оскільки саме цей програміст знайшов правильний спосіб зменшити кількість помилок та знизити вартість обслуговування програми. Використовуючи TDD, ви можете бути абсолютно впевнені, що не пишете ніякого непотрібного коду, оскільки ви писатимете код тільки для проходження тестів. Існує принцип розробки програмного забезпечення під назвою YAGNI (you ain't going to need it), або вам це не знадобиться. TDD запобігає YAGNI. оскільки саме цей програміст знайшов вірний спосіб зменшити кількість помилок та знизити вартість обслуговування програми. Використовуючи TDD, ви можете бути абсолютно впевнені, що не пишете ніякого непотрібного коду, оскільки ви писатимете код тільки для проходження тестів. Існує принцип розробки програмного забезпечення під назвою YAGNI (you ain't going to need it), або вам це не знадобиться. TDD запобігає YAGNI. оскільки саме цей програміст знайшов вірний спосіб зменшити кількість помилок та знизити вартість обслуговування програми. Використовуючи TDD, ви можете бути абсолютно впевнені, що не пишете ніякого непотрібного коду, оскільки ви писатимете код тільки для проходження тестів. Існує принцип розробки програмного забезпечення під назвою YAGNI (you ain't going to need it), або вам це не знадобиться. TDD запобігає YAGNI.

Типовий робочий процес розробки через тестування (TDD)

Що таке TDD та модульне тестування [переклад] - 11
Зрозуміти сенс TDD із суто академічної точки зору складно. Тому розглянемо приклад TDD-сесії. Уявіть собі, що ви сідаєте за стіл і швиденько робите нарис того, що, на вашу думку, буде високорівневим дизайном функції, що дозволяє користувачеві входити в додаток і змінювати свій пароль, якщо він його забуде. Ви вирішуєте, що почнете з першої реалізації функції входу в систему, створивши клас, який оброблятиме всю логіку для процесу входу в систему. Ви відкриваєте свій улюблений редактор і створюєте модульний тест, який називається «Порожній логін не дозволяє користувачеві увійти до системи». Ви пишете код модульного тесту, який спочатку створює екземпляр класу Login (який ви ще не створабо). Потім ви пишете код для виклику методу в класі Login, який передає порожнє ім'я користувача та пароль. Зрештою, ви пишете звірку з очікуваним результатом, перевірку, що користувач із порожнім логіном справді не увійшов до системи. Ви намагаєтеся запустити тест, але він навіть не компілюється, тому що у вас немає класу Login. Ви виправляєте цю ситуацію, і створюєте клас Login разом із методом у цьому класі для входу в систему, а інший – для перевірки стану користувача, щоб дізнатися, чи вони увійшли до системи. Поки що ви не реалізували функціональність цього класу та потрібний нам метод. Ви запускаєте тест на цьому етапі. Тепер він компілюється, але одразу ж видає fail. і створюєте клас Login разом із методом у цьому класі для входу в систему, а інший – для перевірки стану користувача, щоб дізнатися, чи вони увійшли до системи. Поки що ви не реалізували функціональність цього класу та потрібний нам метод. Ви запускаєте тест на цьому етапі. Тепер він компілюється, але одразу ж видає fail. і створюєте клас Login разом із методом у цьому класі для входу в систему, а інший – для перевірки стану користувача, щоб дізнатися, чи вони увійшли до системи. Поки що ви не реалізували функціональність цього класу та потрібний нам метод. Ви запускаєте тест на цьому етапі. Тепер він компілюється, але одразу ж видає fail.
Що таке TDD та модульне тестування [переклад] - 12
Тепер ви повертаєтеся до коду і реалізуєте функціональність, щоб пройти тест. У нашому випадку це означає, що ми повинні отримати результат: «користувач не увійшов до системи». Ви знову запускаєте тест і тепер він проходить. Переходимо до наступного тесту. Тепер уявімо, що вам потрібно написати тест під назвою «Користувач увійшов до системи, якщо він ввів дійсне ім'я користувача та пароль». Ви пишете модульний тест, який створює екземпляр класу Login і намагається увійти до системи з ім'ям користувача та паролем. У модульному тесті ви пишете твердження, що клас Login повинен дати ствердну відповідь на запитання, чи користувач увійшов до системи. Ви запускаєте цей новий тест, і, звичайно, він зазнає невдачі, тому що ваш клас Login завжди повертає, що користувач не увійшов до системи. Ви повертаєтеся у свій клас Login та реалізуєте деякий код для перевірки входу користувача до системи. У цьому випадку вам доведеться дізнатися, як ізолювати цей модуль. На даний момент найпростіший спосіб зробити це – жорстко вказати ім'я користувача та пароль, які ви використовували у своєму тесті, і якщо вони відповідають, то видати результат «користувач увійшов до системи». Ви вносите цю зміну, виконуєте обидва тести, і вони проходять. Приступаємо до останнього кроку: ви дивитеся на створений код і шукаєте спосіб його реорганізації та спрощення. Таким чином, алгоритм TDD: які ви використовували у своєму тесті, і якщо вони відповідають, то видати результат «користувач увійшов до системи». Ви вносите цю зміну, виконуєте обидва тести, і вони проходять. Приступаємо до останнього кроку: ви дивитеся на створений код і шукаєте спосіб його реорганізації та спрощення. Таким чином, алгоритм TDD: які ви використовували у своєму тесті, і якщо вони відповідають, то видати результат «користувач увійшов до системи». Ви вносите цю зміну, виконуєте обидва тести, і вони проходять. Приступаємо до останнього кроку: ви дивитеся на створений код і шукаєте спосіб його реорганізації та спрощення. Таким чином, алгоритм TDD:
  1. Створабо тест.
  2. Написали код для цього тесту.
  3. Зарефакторабо код.

Висновки

Що таке TDD та модульне тестування [переклад] - 13
Це все, що я хотів розповісти про модульне тестування та TDD на цьому етапі. Насправді є багато складнощів, пов'язаних із спробами ізолювати модулі коду, оскільки код буває дуже складним та плутаним. Дуже мало класів існує у повній ізоляції. Натомість у них є залежності, і ці залежності мають залежності і таке інше. Щоб впоратися з такими ситуаціями, ветеран TDD використовує макети-пустушки (Mock), які допомагають ізолювати окремі класи, замінюючи об'єкти залежних модулів. Ця стаття — лише оглядове та дещо спрощене введення в модульне тестування та TDD, ми не вдаватимемося в подробиці про модулі-пустушки та інші методи TDD. Ідея полягає в тому, щоб дати вам основні концепції та принципи TDD та модульного тестування, які, сподіваюся, у вас тепер є. Оригіналhttps://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.