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:
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):
💡 Dobrý jednotkový test je rychlý, izolovaný, deterministický a snadno čitelný.
💡 Integrační testy ověřují, že části systému „spolu hrají“ správně.
💡 E2E testy simulují skutečného uživatele, ale nejsou náhradou za jednotkové testy!
┌───────────────────┐
│ End-to-End testy │ ← málo, pomalé, drahé
└───────────────────┘
┌───────────────────────┐
│ Integrační testy │ ← střední množství
└───────────────────────┘
┌───────────────────────────────────┐
│ Jednotkové testy │ ← hodně, rychlé, levné
└───────────────────────────────────┘
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.
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)); } }
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"
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(); } }
V architektuře mikroslužeb se klasická pyramida rozšiřuje o testovací matici, která zohledňuje:
| Ú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ů.
| 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) |