Як за допомогою тестів пришвидшити реліз

Every Company is Now a Tech Company

Привіт! Я Олег і з 2012 року працюю в тестуванні: почав як Manual QA Engineer, але більшу частину кар'єр кур'єри займаюсь автоматизацією тестування. Веду блог на платформі «Медіум», де пишу нетривіальні статті про тестування українською. Також ви можете знаті мене як автора найпопулярнішого коментаря на ДОУ за 2018 рік.

Для мене тестування — це не просто пошук багів і написання тестів. Це процес удосконалення продукту. У цій статті поділюся порадами, як культура розробки, спрямована на якість, у комбінації з комунікацією між учасниками команди та різні типи тестів допоможуть пришвидшити розробка. Або ні. Альо мало б допомогти, чесно.

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

Continuous delivery стає не просто однією з best practices, а важливою бізнес-вимогою. Як таке можливо? Баланс між культурою компанії, комунікацією між учасниками та технічними рішеннями сприяє швидкій розробці нового функціоналу.

Декомпозиція завдань

Завдання, що приходити на розробка, має бути маленьке. Уявімо, що у вас є фіча кошика в онлайн-магазині, на розробка якої йде 5 днів. У Jira ви можете створити одне завдання на 5 днів і працювати над ним. Або ж розділити його на кілька менших. Мені, приміром, не подобається розв'язків язувати задачі більше як 8-10 годин.

Якщо завдання маленьке, то:

Щоб відповісти на запитання, як саме розбивати завдання, треба спершу зрозуміти контекст команди та проєкту. Мені більше подобається розділяти на бізнес-сценарії, а не технічні процеси: хтось робить фронтенд, а хтось — бекенд. Більше схиляюсь до Full Stack підходу. Тоді зникає проблема комунікацій між FE та BE розробником, коли FE очікує один API, а на бекенді він реалізований по-іншому. Один розробник реалізує API з урахуванням фронтенду краще, бо він його й реалізовує.

Повернемося до прикладу з кошиком в онлайн-магазині. Завдання можна розділити за функціоналом: «Додавання у кошик», «Видалення з кошика», «Оплата», «Очищення кошика». Цей приклад, можливо, банальний, але чудово ілюструє мою ідею.

Головне у цій справі — розбивати завдання на підзавдання, а ті, як це зробити, краще визначити самій команді.

Чітке розуміння бізнесу

Ми часто не знаємо, хто наш кінцевий користувач і що йому потрібно. Відповідно, десь докладаємо забагато зусиль. А десь навпаки — не приділяємо уваги тім фічам, якими клієнти активно користуються. Вся команда має розуміти, як люди використовують продукт. Я стикався з ситуацією, коли операції завантаження файлу приділили забагато уваги. І кілька спринтів реалізовували підтримку різних форматів, валідацій, recovery тощо. Але виявилось, що у 99% випадках користувачі оббирали лише один формат і перезалити файл для них не було проблемою. Водночас ми не зосередилися на формах, які юзери заповнювали, і в базу потрапляли невалідні дані. Тому:

Процес тестування

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

Автотести — швидкі. Юніт-тести виконуються за секунди, UI можуть одночасно працювати у кількох браузерах і бути відносно швидкими. Вони не йдуть пити каву на кухню, а від ві можете :) Автотести ще й точні: їхнє виконання не змінюється, тест щоразу однаковий, що не завжди скажеш про тест, виконаний вручну.

Хто має тестувати

Часто команда складається з кількох розробників, QA. Якщо замовник готовий вкладати в майбутнє, є ще AQA. Процес приблизно такий: розробник займається функціоналом, передає його тестувальнику і перемикається на інше завдання. У тестувальника не запускається білд, він смикає розробника, вони обоє витрачають час, щоб розібратись, хто і що поламав. Вісь ці повернення до, здавалось, вже завершених завдань є перемиканням контексту. На це йде багато часу та зусиль. Згадайте, як вам було важко у понеділок знову заглиблюватися у функціонал, який ви завершили у п'ятому ятницю.

Чому так відбувається:

Автотести — поняття дуже широке, смороду бувають на різних рівнях. Розгляньмо, на яких рівнях і чому треба використовувати тести.

Піраміда тестування

Піраміда тестування — це схема, що розділяє тести за рівнями покриття. З неї ми розуміємо пропорції покриття тестами, час виконання та ціну.

Unit-тести

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

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

Integration-тести

Інтеграційні тести не мають чітких визначень, і навколо них завжди точаться дискусії. Їхня реалізація дуже залежить від системи. У моєму розумінні інтеграційний тест — це white box тест, коли працюємо з кодом системи. Його мета — перевірити взаємодію двох чи більше unit-of-work. Можуть використовуватись зовнішні ресурси: in-memory DB чи щось схоже.

Наприклад, такими тестами можна спробувати тестувати взаємодію DAL та рівня бізнес-логіки. Створюємо фейкову БД для DAL, використовуємо моки для бізнес-логіки і перевіряємо інтеграцію. У всьому іншому ці тести будуть схожі на unit-тести.

Service-тести

У стандартній піраміді тестів цього рівня немає. Unit та integration тести — це white-box тести, тобто вони безпосередньо взаємодіють з кодом. А якщо для тестування підняти лише один сервіс, а не весь продукт, і працювати з ним як з black-box через HTTP чи AMQP? Тоді ми не будемо залежати від сервісу. Потрібно зауважити, що ці тести гарно вписуються у мікросервісну архітектуру, де кожен сервіс незалежний. На шкода, працювати з монолітом буде проблемно, адже підняти лише окрему частину неможливо. У таких тестах легко підставити «фейкові» (спеціально створені для підготовки тестів) ресурси, такі як БД чи Kafka. Наприклад, Kafka піднята у Docker-контейнері чи в окремому хмарному середовищі.

Такі тести не можна використати всюди, але якщо розробляєте мікросервіси чи архітектуру з кількох незалежних або мало залежних сервісів — спробуйте їх. Протестувавши один сервіс ізольовано з фейковими ресурсами, будемо впевненими, що він працює коректно. І якщо так буде з кожним сервісом, можна припустити, що бізнес-логіка працює. Звісно, іноді вилазить проблема — 2 unit tests pass, 1 integration fail. І щоб цього уникнути, є наступні рівні тестів.

Відповідальність за такі тести я б теж покладав на розробників, адже часто компонент чі сервіс є технічним і його нелегко зрозуміти з погляду користувача. Наприклад, коли це Orchestrator Service, що координує роботу кількох сервісів. Його призначення є суто технічним, і без розуміння архітектури неможливо збагнути логіку.

Альо також раджу розділяти цю відповідальність з тестувальниками, які проводять рев'ю тестів чи доповнюють їх.

Contract-тести

Чи траплялося у вас, що сервіс, від якого залежите, змінив свій API? Contract-тести запобігають споріднених проблем. Ідея в тому, що в тесті є дві сторони: Provider і Consumer.

Provider — це сервіс, який надає свій API. За допомогою тесту описується контракт — структура HTTP-запиту та відповіді. Consumer — це сервіс чі сервіси, які споживають API від провайдера. Кожен Consumer описує свій контракт. Ці контракти зіставляюся з контрактом провайдера, і, якщо API змінився, тест падає. Такі тести популярні саме у мікросервісних архітектурах. Але підійдуть і системам, де комунікація між компонентами відбувається за допомогою HTTP.

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

End-to-end тести

У цю категорію потрапляють тести, що виконують повноцінний сценарій так само, як кінцевий користувач. Переважно тут ми працюємо з системою, яку задеплоїли. Сюди належать як API, так і UI-тести.

Ці тести переважно розробляють і підтримують AQA. Альо неправильно покладати на них усю відповідальність. Щонайменше до рев'ю треба залучати розробників, які ознайомлені з тестами та можуть розробляти нові.

Часто на початку проєкту стоїть питання — яку мову програмування зверни? І нерідко є один з двох варіантів: або ту, яку використовував AQA на попередньому проєкті, або ту, якою написаний бекенд. Під час вибору мови програмування потрібно зважати на навички інженерів, які будуть розробляти та підтримувати тести. Якщо розробникі готові допомагати з підтримкою E2E-тестів, поцікавтесь, якою мовою їм буде зручніше їх бачіті. Не думаю, що Ruby-фахівці будуть дуже раді, що ви почали писати тести на C#. Щоб написати тест чи навіть запустити його, розробникам потрібна буде платформа .NET і Visual Studio. Тому, можливо, краще зверни щось з ближчої до них екосистеми.

Варто по-різному підходити до API-тестів і UI-тестів.

API-тести є швидшими та більш стабільними, ніж UI. З їхньою допомогою зручно покривати різні комбінації вхідних даних. Відповідно, якби ми покривали такі сценарії через UI, виконання тестів зайняло б багато часу. Для API-тестів це будуть секунди.

За останні роки з'явились нові інструменти для UI-тестування, і WebDriver, можливо, скоро не буде де-факто стандартом для UI-автоматизації. Такі інструменти, як Cypress, Playwright і Puppeteer, завоювали підтримку ком'юніті. Одна з основних переваг — можливість перехоплювати трафік вебзастосунку і змінювати відповіді бекенду. Під час E2E-тестування проблемою стає підготовка та перевикористання тестових даних. Іноді на створення цих даних чі інфраструктури для їхнього автоматичного створення йшло більше часу, ніж на сам тест. Тому можливість змінювати відповідь бекенду допоможе мінімізувати цю проблему. Звісно, не варто цим зловживати, пам " ятайте, що у вас мають бути тести й зі всіма реальними компонентами.

Антипатерни AQA

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

Уся команда має знати про тести: які вони, де їх знайте, як запустити й де переглянути результат. Це мінімум.

Тести, створені окремо від функціоналу. Мені не подобаються фрази в стилі «у нас автотести відстають від продукту, бо все може змінитись». Якщо все може змінитись, то, може, не варто писати код чі тестувати? Тести треба створювати разом із функціоналом. І важливо, щоб їх можна було легко модифікувати. У такому разі, навіть якщо щось зміниться із годиною — це буде легко оновити.

Багато нестабільних тестів. Часто показником є кількісна характеристика, а не якісна. І якщо у вас є 345 тестів і при кожному запуску більша частина з них падає через нестабільність — вигода від автоматизації сумнівна. Краще мати набагато меншу кількість тестів, але стабільних на 99%. Нестабільні тести — це гірше, ніж відсутність тестів, тому що з часом їм припинять довіряти та заб'ють на них.

Гігантські сценарії. Не варто створювати автотест на десятки кроків. Особливо це стосується UI-тестів, що покривають рідкісній сценарій. Великий тест важко підтримувати, він буває нестабільний і довго виконується. Уникайте таких тестів або оптимізуйте їх за допомогою API чі БД.

Аутсорсинг тест-кейсів. Не варто аутсорсити написання тест-кейсів Manual QA. Це може призвести до того, що ви не будете розуміти бізнес-логіку і писатимете неефективні тести, бо людина, яка писала цей тест-кейс, не знає, як працює автоматизація.

Best practices AQA

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

Summary

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

На початку впровадження цих змін виникне багато проблем — як технічних, так і людських. Людям складно змінювати погляди, особливо якщо раніше вони не інвестували в тестування. Будьте готові до опору.

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

Отже, найголовніше:

Опубліковано: 30/07/20 @ 10:00
Розділ Блоги

Рекомендуємо:

Що потрібно знати тестировщику про рецензування та як його використовувати в роботі
5 книжок, які допомогли зрозуміти зміни у світі, від Павла Кузнєцова, Sr. Product Manager Zalando
Українка – про роботу в Coca-Cola у Сингапурі: "Я відповідаю за Data Science в усьому регіоні Азії та Тихого океану"
Опановуємо основи алгоритмів, або Як прискорити код з 15 до 1000 запитів за секунду
Будуємо власні продукти в сервісній компанії. Наш шлях від нуля до 1 млн $ регулярного річного доходу