Testability та як його покращити?
Test Engineering NotesНавіщо це testability?
З першого погляду у тестувальника і так вистачає клопоту: вимоги опрацюй, плани та стратегії створи, кейси напиши, баги зафіксуй та перевір. Десь поміж цих клопотів ще й треба знайти час на автоматизацію. А ще потрібно демо робити для замовника....
Навіщо навіть перейматися тою тестоздатністю? (так переклав мені Google, не жартую!)
Чи це не задача того, хто цей код пише? Чи можливо тест інженеру вплинути на команду (а особливо на архітекторів та розробників) - та зробити софт трохи більше відкритим та зручним до тестування?
Спробуймо трохи розібратися з цим разом.

Testability: офіційно та не дуже
Яке визначення testability дають стандарти в індустрії?
Згідно ISO/IEC 25010:2011 :
Testability - degree of effectiveness and efficiency with which test criteria can be established for a system, product or component and tests can be performed to determine whether those criteria have been met
Згідно з термінологією ISTQB:
Testability - The capability of the software product to enable modified software to be tested.
Простими словами testability - це те, наскільки окремий компонент та усю систему в цілому, зручно та легко тестувати.
Testability системи залежить від багатьох факторів:
- наскільки можливо контролювати внутрішній стан компонентів під час тестування
- наскільки можливо спостерігати за системою та її компонентами під час тестів
- наскільки можливо тестувати той чи інший компонент в ізоляції
- наскільки повно задокументований компонент та чи можливо написати автоматизовані тести для нього
Testability на рівні коду
Для того, щоб легко тестувати класи на рівні модуля, потрібно мати можливість створювати Mock об'єкти для всіх залежностей класу.
Якщо розробник буде застосовувати Dependency Inversion принцип - то проблем з тестування буде значно менше. В ідеальному випадку, усі залежності повинні передаватися через конструктор класу.
Також не можна забувати про observability та controlability навіть на рівні класу. Розробник (або тест інженер) повинні мати можливість побачити стан критичних частин об'єкта у будь-який момент часу. Причому - не під час запуску системи у debug режимі. Набагато краще буде додати окремі методи для отримання цієї інформації прямо до класу. Але все ж таки - цей аспект повністю під відповідальністю розробника.
Testability на рівні архітектури
На рівні архітектури практичною порадою буде розділяти інфраструктурний код та код домену. Під доменом ми розуміємо увесь код, що відповідає за бізнес-логіку. Інфраструктурний код - це код, який працює з будь-якими зовнішніми залежностями - базами даних, чергами повідомлень, third-party сервісами та ін.
Для того, щоб легко відокремлювати один вид коду від іншого, можна проєктувати компоненти за допомогою Ports and Adapters (Hexagonal) паттерну. Ця модель була запропонована Alistair Cockburn у 2005 році.

Більш практичний приклад такої моделі наведений Maurício Aniche у книзі Effective Software Testing :

Testability очима тестувальників
Гаразд, але як же ж окремо взятий тест інженер може вплинути на testability?
Унікальні property елементів
Якщо ви написали більше одного UI тесту - ви можете багато чого розповісти про біль "довгих та крихких" локаторів. Зміна елементів на сторінці - одна з найголовніших причин нестабільності таких тестів.
Як тест інженер, ви можете зробити в такому випадку дві речі задля покращення testability:
- Домовтеся з командою (або архітекторами) та пропишіть однією з вимог - унікальні property для кожного важливого елементу. Можуть виникнути проблеми з legacy кодом або з тим, як визначити ті самі важливі елементи. Тут вам стане у пригоді наступний крок.
- Домовтеся з розробниками - та додавайте необхідні унікальні property до тих елементів, які вам потрібні в тестах.
Компоненти в ізоляції
Слідкуйте за тим, наскільки зручно тестувати компонент в ізоляції. В ідеальному випадку, розробник чи тестувальник, повинні повністю ізолювати окрему частину (наприклад один мікросервіс) та перевірити тільки бізнес-логіку.
- Чи легко замінити реальну базу даних на in-memory?
- Чи зручно заповнити базу необхідним об'ємом тестових даних для правильної роботи компонента?
- Чи легко замокати зовнішні запити до third-party сервісів?
- Чи зручно відправляти, чи читати повідомлення у Message Queue для окремо взятого сервісу?
API для управління системою
Час від часу потрібно аналізувати те, як ми тестуємо. Те, що окрема бізнес-логіка зав'язана на ймовірностях або специфічних даних іззовні - не повинно бути ані перешкодою для тестування, ані виправданням занадто довготривалому або нестабільному тестуванню.
Можна зробити окремо взятий тестовий API, який дозволить привести систему у той чи інший стан.
Подумайте про це, як систему сейвів у комп'ютерній грі. Замість того, щоб проходити весь рівень кожного разу, задля перевірки босу в кінці - можна ж відразу відправитися у кінець рівня, перемогти боса та виконати необхідні перевірки. В противному разі - процес тестування буде нереально довгим.
А в системах з дуже великою кількістю сутностей, що взаємодіють між собою у реальному часі - ще й дуже нестабільним.
Можна подумати про систему "читів" у контексті Вашого додатка. Це можуть бути як спеціальні команди, які потрібно ввести самому, так і окремі способи створення даних, які система буде обробляти як тестові (навіть у продакшені). Такі дані потім буде легше фільтрувати та аналізувати.