Unit tests

Продовження першої частини матеріалів про Unit tests (UT), яку було опубліковано більше місяця тому.

Чому люди в 2017 році все ще не пишуть тести?

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

Закінчивши з ліричним відступом, можна виділити 2 основні думки:

  1. Юніт тестування не являється чимось особливим, чимось стороннім, додатковим. Під час розробки ми постійно тестуємо код, тому що так побудований спосіб мислення і процес розробки. Це ітеративний процес до написання коду який виконує певну задачу. При цьому ми постійно придумуємо test cases, аналізуємо як код виконує одну чи іншу задачу. В процесі розробки ми постійно створюємо test cases і постійно їх виконуємо порівнюючи результат з тим що ми повинні отримати. Практично, різниця між автоматичним юніт тестом і тим тестуванням яке ми робимо під час розробки  в своїй голові полягає лише в тому що ми його кудись записуємо – unit test як код. Немає ніякого іншого додаткового процесу написання unit test, він завжди являється невід’ємною частиною розробки.
  2. Юніт тестування не являється чимось видуманим просто так чи для забави розробника. Воно являється засобом виконання і реалізації певних вимог до системи. Будь-яка система окрім функціональних вимог містить і нефункціональні, які називаються quality атрибути (атрибути якості). Наприклад, продуктивність або доступність системи. Є ще дві властивості системи які називаються modifiability i maintainability. Modifіability відповідає за те що система модифікаєма, тобото якщо в ню потрібно внести зміни – то в ню можливо внести ці зміни, при чому у певні терміни. Це дає впевненість замовнику що можна буде додати нову функціональність і потратити на це приблизно три місяці. Якщо замовник дізнається що зміну потрібно вносити три роки – то це порушення атрибуту modifіability. Maintainability – систему можна підтримувати. Якщо в ході використання системи в ній знайшлась помилка – то її можна виправити. І її можна виправити в певний часовий проміжок. Очікується що критична помилка може виправитись за 2 дні, але якщо виявляється що її неможливо виправити – то це також порушення нефункціональної вимоги якості системи.

Юніт тести – це один із інструментів як задовольнити нефункціональні вимоги modifability i maintability. Це надає гнучкості системі, адже її потрібно міняти. Інколи не можна внести зміни тому що ми не розуміємо навіщо це було зроблено. В нас немає юніт тестів щоб подивитись кейси навіщо це було зроблено. Або ми не можемо внести зміни тому що не знаємо яка інша частина системи буде зруйновано. Або код, який не має юніт тестів, більш складний, він менш changeable.

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

Чому програмісти все ще не пишуть юніт тести?

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

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

Чому замовникам потрібно доказувати що нам потрібні Unit tests?

Більшість розробки на пост радянському просторі являє собою outsource, при чому, в одному з його найгірших проявів – коли клієнт вирішує як ми будемо працювати. Він дивиться на те як сам девелопив легасі системи 20 років тому і каже що розробляли без якихось юніт тестів і все було нормально. А ми зі свого боку не можемо продати це правильно, не вміємо донести це як невід’ємну частину розробки.

Продаючи клієнту розробників, ми кажеш що ось це чудові кодери, вони пишуть прекрасний код і до нього ж юніт тести і інтеграційні і проводять код рев’ю, але це все забирає додатковий час. На питання “Скільки це забирає часу?” хтось може сказати що відсотків 30. Для замовника це ж значить що 30% часу девелопери, замість того щоб робити його продукт, будуть займатись чимось ще іншим, що йому не змогли впевнено продати. Звісно ж він буде казати щоб це прибрати. Ну а ми ж як завжди кажемо “Клієнт завжди правий”, і прибираємо тести.

Писати UT чи не писати?

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

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

Як впровадити UT на уже існуючий проект?

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

Тести буде важко писати для системи яка розроблялась без testability in mind. Тому можна розпочати з практики – давайти писати тести на весь новий код який ми пишемо. І в цьому випадку ні в кого не буде відмазки що на цей код неможливо написати тести – “я потрачу 3 дні на рефакторінг щоб написати тести”. Потрібно починати з малого. Не можна на проекті який писали 3, а може і 5 років без тестів взяти і написати на все тести.

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

Не існує секретного рецепту як писати тести на legacy код. Це реально буде складно. Буде йти не так як хочеться і взагалі не так як мало б. Буде вимагати часу, якого зазвичай і так не вистачає.

Багато відповідей можна знайти в книзі Working Effectively with Legacy Code від Michael Feathers. Також рекомендую:

Як нам зрозуміти що є якась користь від написання Юніт тестів?

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

Перш ніж писати UT потрібно дати самому собі відповідь на запитання – “А навіщо нам потрібні UT?”. Тому перед початком проводиться фаза розбору поточних проблем на проекті і root cause аналіз. Ми намагаємось зрозуміти в чому проблема і чи можуть її вирішити юніт тести. Побудувавши дерево проблем, яке виходить з необхідності юніт тестів, можна побачити користь від UT, тому що по вітках цього дерева повинно стати легше. Якщо якась вітка вела від дефекта, то почавши писати юніт тести, цих дефектів повинно ставати менше. Якщо програмісти боялись робити рефакторінг, то тепер можна заміряти наскільки простіше це стало робити.

Ефективність від тестів ми будемо відчувати коли потрібно буде щось змінити. Коли код покритий тестами то ми не хвилюємось що щось зробимо не так. Тести завжди покажуть чи код все ще працездатний.

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

Що таке code coverage і як правильно його використовувати?

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

По хорошому, закінчивши задачу, розробник повинен прогнати code coverage і той повинен показати йому сценарії які він не врахував.

Цифра покриття, по суті, нічого не значить. Значення має звіт, який ми читаємо і розуміємо. Він показує куски коду які не покриті. Можна побачити що якийсь кусок коду критичний і потребую тесту, інший ж не критичний і не потребує. Покриття повинне показувати код який ще не покрили. Відсоток покриття може становити і 90%, але є 10% критичного коду який не покритий. В цьому випадку використання code coverage неправильне.

Схожі статті