article-spots
article-carousel-spots
programs
Технології

Піраміда тестування, пісочний годинник чи ріжок морозива — "правильні" та "неправильні" підходи до тестування

17 бер

Переконатися у якості програмного забезпечення — важливо для кожного, хто працює в IT. Чи ви майбутній розробник, чи тестувальник, чи навіть аналітик — знання про підходи до тестування точно стане вам у нагоді. Вадим Настоящий, Senior Test Automation Engineer в EPAM, знайомить нас із пірамідою тестування та іншими моделями більш детально. 

Що таке піраміда тестування? 

Піраміда тестування — це візуальна метафора, яка описує ідеальний розподіл типів тестів програмного забезпечення на трьох рівнях: модульні тести (юніт), інтеграційні тести та наскрізні тести (e2e, або системні тести). Форма піраміди ілюструє кількість тестів, яку ви повинні мати на кожному рівні: більше внизу (модульні тести), менше вгорі (наскрізні тести). 

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

 

A diagram of a pyramid

Description automatically generated 

Розглянемо детальніше рівні піраміди тестування: 

  • Модульні тести, юніт тести (нижній рівень). Ці тести охоплюють найменші частини програми, як правило, окремі функції або методи. Юніт-тести швидко виконуються, їхнє завдання — перевірити, що кожен фрагмент коду виконує свою функцію правильно. 

Приклад: toLowerCase() — це функція, яка отримує один параметр str типу string і повертає string або null. Функція перевіряє, чи вхідні дані є типом string. Якщо так, то вона перетворює рядок у нижньому регістрі. Якщо вхідні дані мають null або undefined тип, функція повертає null. Якщо вхідні дані мають інший тип (не string), функція повертає вхідні дані без змін. 

const toLowerCase: IDataTransformer = (str: string) => { 
 let result = str ?? null 
 if (typeof result === 'string') { 
   result = result.toLowerCase() 
 } 
 return result 
  
describe('data.toLowerCase', () => { 
   it('returns a string in lower case format', () => { 
     expect(data.toLowerCase('ANY')).toBe('any') 
   }) 
   it('returns null for null input', () => { 
     expect(data.toLowerCase(null)).toBeNull() 
   }) 
   it('returns null for undefined input', () => { 
     expect(data.toLowerCase(undefined)).toBeNull() 
   }) 
   it('returns input without changes if not a string given', () => { 
     expect(data.toLowerCase([])).toStrictEqual([]) 
     expect(data.toLowerCase({})).toStrictEqual({}) 
     expect(data.toLowerCase(true)).toBe(true) 
   }) 
 }) 
  •  Інтеграційні тести (середній рівень). Як випливає з назви, інтеграційні тести перевіряють, як різні модулі або сервіси працюють разом. Ці тести є життєво важливими, оскільки вони допомагають виявити проблеми, що виникають при взаємодії модулів. 

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

const axios = require('axios') 
const expect = require('chai').expect 
 
const baseURL = 'http://localhost:3000/api' 
 
describe('Calculator API Integration Tests', () => { 
 describe('Addition Operation', () => { 
   it('should add two numbers correctly', async () => { 
     const requestBody = { 
       operation: 'add', 
       numbers: [5, 3] 
     }; 
 
     try { 
       const response = await axios.post(`${baseURL}/calculate`, requestBody) 
 
       expect(response.status).to.equal(200) 
 
       expect(response.data.result).to.equal(8) 
 
     } catch (error) { 
       throw new Error('Failed to communicate with the calculator API') 
     } 
   }) 
 }) 
}) 
  •  Наскрізні тести, системні тести (верхній рівень). Ці тести імітують реальні сценарії користувачів від початку до кінця, забезпечуючи правильне функціонування системи в цілому. Вони є найбільш комплексними, але також найбільш трудомісткими та ресурсоємними. 

 Приклад: Наскрізний тест може імітувати вхід користувача в систему 

import HomePage from '../src/pages/HomePage'; 
import { Accounts } from '../src/Accounts'; 
import Credentials from '../src/Credentials'; 
 
describe('Login and Logout tests', () => { 
   it('Login and Logout first user in page', () => { 
       const homePage: HomePage = new HomePage(); 
       const { name, password } = Credentials.getCredentials(Accounts.Active); 
 
       homePage 
           .visit() 
           .checkPageUrl() 
           .header.clickOnLogInButton(); 
 
       homePage.logInModal 
           .logInWithCredentials(name, password) 
           .header.checkUserName(name) 
           .clickOnLogOutButton(); 
 
       homePage 
           .checkPageUrl() 
           .header.checkLogInButton(); 
   }); 
}); 

Відмінності між рівнями та їх важливість 

Швидкість і обсяг: Юніт-тести є найшвидшими та найвужчими за обсягом. Наскрізні тести, будучи найповільнішими, охоплюють всю функціональність програми. 

Вартість та підтримка: Модульні тести дешевші у виконанні та простіші в підтримці, водночас наскрізні тести можуть бути дорогими і складними через їхню залежність від повністю інтегрованих систем. 

Ізоляція несправностей: Модульні тести краще підходять для ізоляції дефектів на мікрорівні; інтеграційні та наскрізні тести діагностують проблеми у взаємодії та загальному досвіді відповідно. 

Ідеальний розподіл тестів: Загалом, рекомендований розподіл тестів за пірамідою тестування виглядає наступним чином: 

  • Модульні тести: 70-80% 
  • Інтеграційні тести: 15-20% 
  • Наскрізні тести: 5-10% 

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

А тепер найцікавіше — чого уникають тестувальники? 

1. "Ice cream cone" (морозиво) — у контексті піраміди тестування використовується для ілюстрації дисбалансу в стратегії тестування, де кількість вищорівневих тестів (наприклад, UI-тестів або приймальних тестів) значно перевищує кількість нижчорівневих тестів (таких, як одиничні тести). В ідеальній піраміді тестування, база має бути широкою, що свідчить про велику кількість одиничних тестів, середина помірною, що відображає інтеграційні тести, а вершина вузькою, що представляє кількість приймальних тестів. 

 

  

Ice cream cone у контексті піраміди тестування вважається поганим підходом через декілька причин: 

  1. Неефективність: Коли більша частина зусиль спрямована на вищорівневе тестування, можливі проблеми з ефективністю. UI-тести часто більш часозатратні та крихкі порівняно з одиничними тестами. 
  2. Висока вартість підтримки тестів: Тести на рівні UI вимагають більше часу і зусиль для виправлення та оновлення при змінах у додатку, що збільшує загальну вартість підтримки тестового набору. 
  3. Пізнє виявлення помилок: Оскільки більшість тестів проводиться на високому рівні, проблеми з програмним забезпеченням можуть бути виявлені пізніше у процесі розробки, що ускладнює їх виправлення (а ще — збільшує вартість виправлення). 
  4. Недостатня перевірка: Оскільки одиничні тести та інтеграційні тести дають можливість глибше перевіряти логіку та взаємодію компонентів в ізоляції, ігнорування цих рівнів може призвести до непомічених проблем в архітектурі та функціональності. 

2. "Пісочний годинник" — у тестуванні програмного забезпечення має зовсім іншу структуру порівняно з традиційною пірамідою тестування. Ця модель зображає розподіл типів тестування, де зосереджено велику кількість одиничних тестів (на нижньому рівні), помірну кількість інтеграційних тестів (у середині), та значну кількість вищерівневих тестів (на верхньому рівні), таких як системні та приймальні тести. 

 

 

  

Ось декілька основних моментів щодо моделі "пісочний годинник": 

  1. Охоплення одиничними тестами: Нижня частина пісочного годинника широка, що означає сильний фокус на одиничні тести. Велика кількість одиничних тестів гарантує, що кожен компонент системи ретельно перевіряється в ізоляції. Це допомагає у виявленні та виправленні багів на ранній стадії. 
  2. Міцні інтеграційні тести: Середня частина пісочного годинника вужча, що вказує на помірний рівень інтеграційних тестів. Це важливо для перевірки взаємодії між компонентами та виявлення проблем, які можуть виникнути внаслідок інтеграції. 
  3. Широке верхів’я для вищерівневих тестів: Верхня частина пісочного годинника є широкою, аналогічною нижній частині. Це підкреслює важливість системних і приймальних тестів, які включають поведінку цілого додатку чи системи. Такі тести можуть охоплювати випробування користувацького інтерфейсу, роботи з базами даних та інших аспектів прикладання з точки зору кінцевого користувача. 

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

  

Висновки

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


А якщо ви готові зануритись у світ тестування програмного забезпечення і побудувати успішну кар'єру в цій сфері — чекаємо на навчальних програмах EPAM Campus!