Psaní testů s AI – pytest a Jest, které opravdu něco testují
Jak nechat AI generovat unit testy, aby nebyly jen pro pokrytí. Prompty, parametrizace, okrajové případy, mutační testování a typické chyby AI testů.
AI umí vygenerovat stovky řádků testů za pár sekund. Problém je, že spousta z nich testuje, že kód dělá to, co dělá – včetně chyb. Taková sada zvýší pokrytí, ale chybu nechytí. Tady je postup, jak z AI dostat testy, které mají hodnotu.
Hlavní past: testy opsané z implementace
Když AI dostane funkci a pokyn „napiš testy“, často vezme aktuální chování jako správné:
def discount(price, percent):
return price - price * percent / 10 # chyba: má být / 100
# AI test odvozený z implementace
def test_discount():
assert discount(200, 10) == 0 # "prošlo" – a test chybu zabetonoval
Řešení: dejte AI specifikaci, ne jen kód. Napište, co funkce má dělat, a nechte testy vycházet z toho.
Prompt, který funguje
Napiš testy v pytest pro funkci discount(price, percent).
Specifikace:
- price je Decimal >= 0, percent je int 0–100
- vrací cenu po slevě zaokrouhlenou na 2 desetinná místa (ROUND_HALF_UP)
- percent mimo rozsah 0–100 → ValueError
- záporná cena → ValueError
Požadavky:
- testy odvozuj ze specifikace, ne z implementace
- použij @pytest.mark.parametrize pro sadu vstupů
- pokryj okrajové hodnoty (0, 100, 0.005 zaokrouhlení) a chybové stavy
- každý test testuje jednu věc a má popisný název
- nepoužívej mocky, funkce je čistá
Jak vypadá dobrý výsledek
from decimal import Decimal
import pytest
from shop.pricing import discount
@pytest.mark.parametrize("price, percent, expected", [
(Decimal("200"), 10, Decimal("180.00")),
(Decimal("99.99"), 0, Decimal("99.99")),
(Decimal("50"), 100, Decimal("0.00")),
(Decimal("0.05"), 50, Decimal("0.03")), # 0.025 → 0.03 (HALF_UP)
])
def test_discount_valid(price, percent, expected):
assert discount(price, percent) == expected
@pytest.mark.parametrize("percent", [-1, 101])
def test_discount_rejects_percent_out_of_range(percent):
with pytest.raises(ValueError):
discount(Decimal("100"), percent)
def test_discount_rejects_negative_price():
with pytest.raises(ValueError):
discount(Decimal("-1"), 10)
Očekávané hodnoty si ověřte ručně. Právě u nich AI chybuje nejčastěji – špatně spočítá zaokrouhlení nebo datum.
Ekvivalent v Jestu
import { discount } from '../src/pricing';
describe('discount', () => {
test.each([
[200, 10, 180],
[99.99, 0, 99.99],
[50, 100, 0],
])('discount(%p, %p) = %p', (price, percent, expected) => {
expect(discount(price, percent)).toBeCloseTo(expected, 2);
});
test('throws for percent > 100', () => {
expect(() => discount(100, 101)).toThrow(RangeError);
});
});
U floatů v JS používejte toBeCloseTo, ne toBe – 0.1 + 0.2 není přesně 0.3.
Na co AI testy nejčastěji kazí
| Problém | Jak poznat | Oprava |
|---|---|---|
| Testy z implementace | Očekávané hodnoty „sedí“ i na zjevné chybě | Dát specifikaci |
| Přemockování | Mockuje se i testovaná funkce nebo vše kolem | Mockovat jen hranice (síť, čas, DB) |
| Test bez asserce | Volá kód, ale nic neověřuje | Ke každému testu konkrétní assert |
| Neexistující API | pytest.raises(..., msg=), vymyšlené matchery |
Spustit a opravit |
| Křehké testy | Závisí na čase, pořadí, náhodě | freezegun, fixní seed, sorted() |
| Jeden obří test | 20 assertů v jedné funkci | Rozdělit / parametrizovat |
Ověřte, že testy něco chytí
Pokrytí kódu (pytest --cov, jest --coverage) říká jen, které řádky se spustily, ne že se ověřily. Lepší kontrola je mutační testování: nástroj záměrně rozbije kód (změní < na <=, + na -) a sleduje, zda testy selžou.
# Python
pip install mutmut
mutmut run
mutmut results
# JavaScript / TypeScript
npx stryker init
npx stryker run
Přeživší mutanti = místa, kde testy nic nehlídají. Ty pak dejte AI se zadáním „napiš test, který tento mutant zabije“.
Rychlejší ruční varianta: v kódu schválně udělejte chybu a spusťte testy. Pokud projdou, něco chybí.
Testy k chybě: nejdřív červená
Při opravě chyby postupujte takto:
- Požádejte AI o test, který chybu reprodukuje.
- Spusťte ho – musí selhat. Pokud projde, netestuje danou chybu.
- Opravte kód.
- Test projde → máte ochranu proti návratu chyby.
Co testovat ručně psaným testem
AI je silná v generování variant vstupů a okrajových případů. Slabší je v testech, které vyžadují pochopení byznysu – „uživatel s prošlým předplatným nesmí stáhnout export“. Tyto testy napište nebo aspoň zadejte sami, AI pak doplní varianty.
V ColdHost AI můžete v Projektu nahrát kód i specifikaci (třeba jako PDF nebo text) a v režimu Programování nechat vygenerovat testy přímo do souborů projektu.