Obsah
Testovací pyramida
Testovací pyramida je koncept v softwarovém inženýrství, který vizualizuje doporučené poměry a hierarchii různých typů testů v softwarovém projektu. Tento model poprvé formuloval Mike Cohn ve své knize *Succeeding with Agile* (2009) a od té doby se stal základním principem pro budování udržitelné a efektivní testovací strategie.
Cílem testovací pyramidy je:
- minimalizovat náklady na testování,
- maximalizovat rychlost zpětné vazby,
- zvýšit spolehlivost testovací sady,
- a podpořit rychlý vývoj s vysokou kvalitou.
Struktura testovací pyramidy
Pyramida se skládá ze tří hlavních vrstev, uspořádaných od nejrychlejších a nejlevnějších testů (spodek) po nejpomalejší a nejnákladnější (vrchol):
1. Jednotkové testy (Unit Tests) – základna pyramidy
- Co testují: Izolované jednotky kódu (např. funkce, metody, třídy).
- Rychlost: Milisekundy – spouští se běžně stovky až tisíce za sekundu.
- Náklady na údržbu: Nízké.
- Stabilita: Vysoká – selhávají jen při změně logiky.
- Doporučený podíl: 70 % a více všech automatizovaných testů.
💡 Dobrý jednotkový test je rychlý, izolovaný, deterministický a snadno čitelný.
2. Integrační testy (Integration Tests) – střední vrstva
- Co testují: Interakce mezi více komponentami nebo systémy (např. volání databáze, API, služby třetích stran).
- Rychlost: Desítky milisekund až sekundy.
- Náklady na údržbu: Střední – závisí na složitosti integrací.
- Stabilita: Střední – mohou selhat kvůli externím závislostem.
- Doporučený podíl: 20–25 % testů.
💡 Integrační testy ověřují, že části systému „spolu hrají“ správně.
3. End-to-end (E2E) testy – vrchol pyramidy
- Co testují: Kompletní uživatelské scénáře napříč celou aplikací (např. „uživatel se přihlásí, přidá zboží do košíku a zaplatí“).
- Rychlost: Sekundy až minuty.
- Náklady na údržbu: Vysoké – citlivé na změny UI.
- Stabilita: Nízká – často selhávají kvůli dočasným problémům (časování, síť).
- Doporučený podíl: 5–10 % testů.
💡 E2E testy simulují skutečného uživatele, ale nejsou náhradou za jednotkové testy!
Vizuální znázornění pyramidy
┌───────────────────┐
│ End-to-End testy │ ← málo, pomalé, drahé
└───────────────────┘
┌───────────────────────┐
│ Integrační testy │ ← střední množství
└───────────────────────┘
┌───────────────────────────────────┐
│ Jednotkové testy │ ← hodně, rychlé, levné
└───────────────────────────────────┘
Konkrétní příklady testů
Jednotkový test v Pythonu (s pytest)
Soubor: `calculator.py`
def divide(a, b): if b == 0: raise ValueError("Dělení nulou není povoleno") return a / b
Soubor: `test_calculator.py`
import pytest from calculator import divide def test_divide_positive_numbers(): assert divide(10, 2) == 5.0 def test_divide_by_zero_raises_error(): with pytest.raises(ValueError, match="Dělení nulou není povoleno"): divide(5, 0)
✅ Rychlé, izolované, bez závislostí – ideální jednotkový test.
Jednotkový test v Javě (s JUnit 5)
Soubor: `BankAccount.java`
public class BankAccount { private double balance; public BankAccount(double initialBalance) { this.balance = initialBalance; } public void withdraw(double amount) { if (amount > balance) { throw new IllegalArgumentException("Nedostatek prostředků"); } balance -= amount; } public double getBalance() { return balance; } }
Soubor: `BankAccountTest.java`
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class BankAccountTest { @Test void withdraw_ValidAmount_ReducesBalance() { BankAccount account = new BankAccount(100.0); account.withdraw(30.0); assertEquals(70.0, account.getBalance()); } @Test void withdraw_InsufficientFunds_ThrowsException() { BankAccount account = new BankAccount(50.0); assertThrows(IllegalArgumentException.class, () -> account.withdraw(60.0)); } }
Integrační test v Pythonu (s databází pomocí Testcontainers)
import pytest from sqlalchemy import create_engine, text from myapp.models import User @pytest.fixture(scope="module") def db_engine(): # Spustí Docker kontejner s PostgreSQL (přes testcontainers) with PostgresContainer("postgres:15") as postgres: engine = create_engine(postgres.get_connection_url()) yield engine def test_user_persistence(db_engine): # Vložíme uživatele do DB with db_engine.connect() as conn: conn.execute(text("INSERT INTO users (name) VALUES ('Alice')")) result = conn.execute(text("SELECT name FROM users WHERE name = 'Alice'")) assert result.fetchone()[0] == "Alice"
E2E test v Javě (s Playwright)
import com.microsoft.playwright.*; import org.junit.jupiter.api.*; public class LoginE2ETest { static Playwright playwright; static Browser browser; @BeforeAll static void launchBrowser() { playwright = Playwright.create(); browser = playwright.chromium().launch(); } @Test void successfulLogin() { Page page = browser.newPage(); page.navigate("https://mojeaplikace.cz/login"); page.fill("#username", "admin"); page.fill("#password", "heslo123"); page.click("button[type='submit']"); assertTrue(page.url().contains("/dashboard")); assertTrue(page.textContent("h1").contains("Vítejte")); } @AfterAll static void closeBrowser() { playwright.close(); } }
Testovací matice pro mikroslužby
V architektuře mikroslužeb se klasická pyramida rozšiřuje o testovací matici, která zohledňuje:
- Vertikální vrstvy (jednotkové → integrační → E2E),
- Horizontální dimenzi (testování uvnitř služby vs. mezi službami).
| Úroveň testování | Uvnitř služby | Mezi službami (síť) |
| Jednotkové testy | Test logiky bez závislostí | – |
| Integrační testy | Test s databází, cache, externí API | Kontraktové testy (Pact), Consumer-Driven Contracts |
| E2E / Špičkové testy | – | Kompletní workflow přes více služeb (např. objednávka → platba → doručení) |
🔑 Klíčový princip: Nejdříve otestujte logiku uvnitř služby, pak ověřte smlouvy mezi službami, a až nakonec spusťte nákladné E2E testy.
Tento přístup minimalizuje závislost na „živém“ prostředí a zvyšuje izolaci testů.
Proč dodržovat testovací pyramidu?
- Rychlá zpětná vazba: Jednotkové testy selžou během sekund, nikoli minut.
- Nižší náklady: Údržba 1000 jednotkových testů je levnější než 10 E2E testů.
- Lepší laditelnost: Když selže jednotkový test, víte přesně, kde je chyba.
- Vyšší pokrytí: Lze snadno pokrýt okrajové případy (edge cases).
- Bezpečný refaktoring: Spolehlivá síť jednotkových testů umožňuje bez obav upravovat kód.
Běžné antipatterny
Testovací kužel (Ice-Cream Cone)
- Příliš mnoho manuálních nebo E2E testů,
- Nedostatek jednotkových testů,
- Důsledek: Pomalé buildy, nestabilní testy, nízká důvěra v automatizaci.
Testovací kobliha (Testing Cupcake)
- Tým má jen E2E testy a žádné jednotkové/integrační,
- Vývoj je pomalý, chyby se hledají hodiny.
Přehnaná automatizace UI
- Každý požadavek se testuje E2E testem,
- Vede k „testovací dlužné pasti“ – testy jsou dražší než kód samotný.
Nástroje podle vrstvy
| Vrstva | Příklady nástrojů |
| ——– | ——————- |
| Jednotkové testy | JUnit, pytest, NUnit, Jest, Mocha |
| Integrační testy | Testcontainers, Pact, Postman, REST Assured |
| E2E testy | Cypress, Playwright, Selenium, WebdriverIO |
| BDD testy | Cucumber, SpecFlow, Behave (často nad E2E nebo integrační vrstvou) |
Související pojmy
Externí odkazy
- Martin Fowler – Test Pyramid: https://martinfowler.com/bliki/TestPyramid.html
- Mike Cohn – Original article: https://mountaingoatsoftware.com/blog/the-forgotten-layer-of-the-test-automation-pyramid
- Google Testing Blog – Just Say No to More End-to-End Tests: https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html
- Pact.io – Contract testing for microservices: https://pact.io
