Фото від Elena Cordery
Я робив доповідь на основі цієї публікації яку можна подивитись тут:
Нещодавно, Ґільєрмо Рауч (творець Socket.io та засновник Zeit.co (компанії що стоїть за масою виняткових речей що виходили останнім часом)) твітнув щось фундаментальне:
Пишіть тести. Не замало. Не забагато. Переважно інтеграційні
Це глибоко, хоч і коротко, отже вйо до деталей:
Так, для більшості проєктів вам слід писати автоматичні тести. Слід, якщо ви хоч трохи цінуєте свій час. Набагато краще спіймати ваду локально тестами ніж приймати дзвінок о 2:00 ранку і тоді виправляти її. Я відчуваю що економлю час коли працюю над написанням тестів. Це може зайняти або ж не займати більше часу, аніж розробка, проте я (та інші) майже напевно заощаджуватимуть час підтримуючи їх.
Річ, про яку потрібно замислюватись коли пишете тести, це скільки впевненості вони надають вам в те що проєкт не містить вад. Статична типізація та інструменти підсвічування помилок кшталту TypeScript або ESLint можуть дати вам неабиякий рівень упевненості і якщо ви не користуєтесь цими інструментами я дуже раджу поглянути до них. Однак, навіть строго типізовані мови повинні мати тести. Типізація та підсвічування помилок не можуть гарантувати що ваша бізнес-логіка не містить вад. Таким чином, ви все ще можете серйозно підвищити свою впевненість за допомогою хорошого набору тестів.
Я чув, що менеджери та команди вимагають 100%-ве покриття коду тестам. Це дуже погана ідея. Проблема полягає в тому що віддача від тестів спадає як тільки покриття перевищує 70% (Я видумав це число... жодного наукового підґрунтя в ньому нема). Чому так? Ну коли ви прагнете до 100% увесь час то виявляєте, що проводите час тестуючи речі які насправді тестувати не потрібно. Речей які фактично взагалі не містять логіки (а отже будь-які вади можуть спіймати ESLint та Flow). Підтримка таких тестів дійсно дуже сповільнює вас та вашу команду.
Ви також можете виявити що тестуєте деталі реалізації тільки для того щоб мати в тестовому середовищі цей один рядок коду що складно відтворити. Вам дійсно варто уникати тестування деталей реалізації тому що воно не дає вам багато впевненості у тому що ваш додаток працює, і до того ж це сповільнює рефакторинг. Тести мають мінятись напрочуд рідко коли виконується рефакторинг.
Слід зазначити, що майже всі мої проєкти з відкритим кодом мають 100%-ве покриття тестами. Це тому, що більшість з них є невеликим бібліотеками та інструментами, які можна перевикористовувати у багатьох різних ситуаціях (несправність може спричинити серйозну проблему у багатьох проєктах користувачів), та й у всякому разі покриття у 100% відносно легко на них отримати.
Існують всілякі різноманітні типи тестування (зацініть мою 5-хвилинну доповідь про це на Fluent Conf: "Що можна дізнатися про тестування від колеса"). У кожного з них є свої компроміси. Три найпоширеніші форми тестування, про які ми говоримо, коли йдеться про автоматизоване тестування, це: unit, інтеграційне та E2E.
Ось слайд з мого воркшопу для FrontendMasters: "Тестування JavaScript додатків".
Ця піраміда тестування — комбінація тієї яку я взяв з блогу Мартіна Фавлера та тієї які я взяв з блогу Google Testing.
Як зазначено на слайді, піраміда показує з низу до верху: unit, інтеграційне та E2E тестування. Просуваючись вгору пірамідою, тести стають повільнішими для написання/запуску та дорожчими (у плані часу та ресурсів) для виконання/підтримки. Це означає, що з огляду на ці фактори, вам слід витрачати більше часу на unit-тести.
Одна річ яку вона, проте, не розкриває — це те що просування пірамідою вгору збільшує коефіцієнт упевненості від кожного типу тестів. Ви отримуєте більше вигоди. Тож, хоча E2E-тести можуть бути повільнішими та дорожчими, аніж unit-тести, вони приносять вам набагато більше впевненості у тому що ваша програма працює належним чином.
Як зазначено у твіті, наші інструменти вийшли за рамки припущення оригінальної концепції піраміди тестування Мартіна. Ось чому я створив «Кубок тестування» 🏆
Ось інша жартівлива ілюстрація важливості інтеграційних тестів:
Не має значення, чи ваш компонент <A />
відмальовує компонент <B />
з властивостями c
і d
, якщо компонент <B />
фактично ламається, якщо властивість c
не передається. Тож хоча наявність unit-тестів для перевірки роботи компонентів в ізоляції не є поганою річчю, користі від того буде не багато якщо ви також не перевірятимете їх роботу належним чином укупі. І ви помітите, що маючи перевірку на коректну роботу компонентів один з одним, вам часто не потрібно перейматись їх тестуванням в ізоляції.
Інтеграційні тести забезпечують чудовий баланс компромісів між впевненістю та швидкістю/витратами. Ось чому бажано витрачати на них більшу частину (не всі, майте на увазі) своїх зусиль.
Докладніше про це читайте у тестуванні деталей реалізації. Додаткову інформацію про різні відмінності тестів можна отримати зі статті про статичне проти unit проти інтеграційного про E2E тестування для фронтенд додатків
Межа між інтеграційними та unit-тестами дещо розмита. Попри це, я вважаю, що найкраще, що можна зробити для написання більшої кількості інтеграційних тестів — це припинити підмінювати стільки всього. Підмінюючи щось, ви відбираєте всю впевненість в інтеграції між тим, що тестуєте, і тим, що підмінюється. Я розумію, що іноді це може допомогти (хоча дехто не погоджуються). Вам дійсно не варто надсилати електронні листи чи стягувати плату з кредитних карток під час кожного тесту, але здебільшого є можливість уникати підміни, і краще її використовувати.
Якщо ви працюєте з React, тоді це також включає неглибокий рендеринг. Докладніше про це читайте у Чому я ніколи не використовую неглибокий рендеринг.
Я не думаю, що хтось може стверджувати, що тестування програмного забезпечення — це марна трата часу. Найбільший виклик полягає в тому, щоб знати що тестувати і як це робити таким чином щоб отримати справжню впевненість, а не помилкову віру у тестування деталей реалізації.
Сподіваюся що цей матеріал стане вам у пригоді, і я бажаю вам успіху у прагненні знайти впевненість у випуску ваших додатків!