Dio 1 – Dobar jedinični test
Dio 2 – Alati pytesta
Dio 3 – Razvoj vođen testovima
Praksa – TDD u DeliveryApp
Materijale priredio Erkan Islamović
erkan.islamovic@eubd.edu.ba · erkan.islamovic@gmail.com
Petnaest sedmica: svaka ima tri časa teorije i tri časa praktične nastave. Danas: poglavlje 5 (Sedmica 5, označeno). Isti primjer kroz knjigu: DeliveryApp, narudžba N-1001.
| Sed. | Poglavlje | Teorija (3 časa) | Praksa (3 časa) |
|---|---|---|---|
| 1 | Zašto testiramo | Uvodne priče; greška, defekt, otkaz; sedam principa; DeliveryApp | Tabela očekivanih cijena rukom |
| 2 | Proces i nivoi | Test proces; nivoi i V-model; vrste; statičko testiranje | Recenzija zahtjeva DeliveryApp |
| 3 | Manuelno testiranje | Test slučaj; izvještaj o defektu; istraživačko testiranje | Test slučajevi za web stranicu |
| 4 | Automatizovano testiranje | Šta automatizovati; piramida testova; prvi test | Prvi pytest |
| 5 | Jedinično testiranje | AAA; fixture, parametri, izuzeci; TDD | Testovi za dostavu i popust |
| 6 | Bijela kutija | Graf toka; pokrivenost; ciklomatska složenost | coverage.py do 100 % grana |
| 7 | Tok podataka | Definicija i upotreba; anomalije; statička analiza, mutacije | ruff, mypy, ručna mutacija |
| 8 | Crna kutija | Ekvivalencija i granice; tabela odlučivanja; stanja | Lov na pet grešaka |
| Sed. | Poglavlje | Teorija (3 časa) | Praksa (3 časa) |
|---|---|---|---|
| 9 | Model i integracija | Model; strategije integracije; API | TestClient za API |
| 10 | Sistemsko testiranje | Sistemsko; performanse i sigurnost; prihvatno i BDD | Locust, Gherkin |
| 11 | OO testiranje | Stanje objekta; nasljeđivanje; dvojnici | Narudzba, unittest.mock |
| 12 | Paralelne aplikacije | Trka podataka; zastoj; nestabilni testovi | Niti, 1000 ponavljanja |
| 13 | Web aplikacije | Web i DOM; Playwright; Page Object, pristupačnost | Playwright za DeliveryApp |
| 14 | Upravljanje testiranjem | Test plan i rizik; metrike; CI/CD | GitHub Actions |
| 15 | Alati, AI, projekat | Pregled alata; AI u testiranju; ISTQB | Odbrana projekta |
U jesen 1997. godine dva programera sjela su u avion od Ciriha do Atlante, na putu na veliku konferenciju o objektnom programiranju OOPSLA. Jedan je bio Kent Beck, Amerikanac koji je nekoliko godina ranije za jezik Smalltalk napisao mali alat za testiranje. Drugi je bio Erich Gamma, Švicarac, jedan od četiri autora slavne knjige Design Patterns. Beck je htio naučiti Javu, a Gamma je htio vidjeti Beckov alat za testiranje. Umjesto da gledaju filmove, otvorili su laptop i programirali u paru, nekoliko sati, iznad Atlantika. Pisali su na način koji je Beck propovijedao: prvo test, pa kod koji ga zadovolji.
Kad je avion sletio, imali su prvu verziju alata JUnit. Taj mali alat, napravljen tokom jednog leta, postao je jedan od najkorištenijih programa u historiji Jave, a po njegovom uzoru nastali su alati za skoro svaki jezik: NUnit za C#, PHPUnit, CppUnit, unittest za Python. Cijela porodica zove se xUnit. Pouka priče nije samo da se dobre stvari mogu napraviti brzo. Pouka je da je jedinični test toliko jednostavna ideja da stane u let avionom: mali program koji provjeri mali dio drugog programa, i to radi svaki put kad ga pozovete.
U poglavlju 4 napisali smo prve automatske testove u pytestu i vidjeli piramidu testova: najviše testova treba biti u podnožju, na nivou jedinice. Ovo poglavlje uči kako se ti testovi pišu dobro. Prvo vidimo šta je jedinica i kako izgleda dobar jedinični test (obrazac pripremi–izvrši–provjeri). Zatim alate pytesta koji štede posao: parametrizaciju, provjeru izuzetaka i fixture. Na kraju upoznajemo razvoj vođen testovima (TDD), način rada iz Beckovog aviona, i njime pravimo novu funkciju DeliveryApp: napojnicu za dostavljača.
raise ValueError).Nakon poglavlja možete:
reći šta je jedinica i zašto se testira izolovano; napisati jedinični test po obrascu pripremi–izvrši–provjeri; dati testu ime koje kaže šta provjerava; jednim parametrizovanim testom provjeriti više vrijednosti; provjeriti da funkcija javlja izuzetak za nedozvoljen ulaz; napraviti fixture za zajedničku pripremu; objasniti zašto se novac čuva u centima, a ne u decimalnim brojevima; objasniti ciklus crveno–zeleno–refaktoriši; razviti funkciju napojnice postupkom TDD, korak po korak; reći šta jedinični testovi ne mogu otkriti.
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 5.1–5.3 Jedinica; pripremi–izvrši–provjeri; imena testova | Test klase Narudzba |
| Teorija 2 | 5.4–5.6 Parametrizacija; izuzeci; fixture; zašto centi | 0.1 + 0.2 ≠ 0.3 |
| Teorija 3 | 5.7–5.9 TDD; napojnica korak po korak; granice jediničnih testova | Crveno, zeleno, refaktoriši uživo |
Jedinica je najmanji dio programa koji ima smisla testirati odvojeno. U Pythonu je to najčešće jedna funkcija (cijena_dostave, popust) ili jedna klasa s metodama (Narudzba). Jedinični test provjerava jednu jedinicu, izolovano: bez baze podataka, bez mreže, bez ekrana, bez drugih servisa.
Zašto izolovano? Tri razloga:
cijena_dostave izvrši se za milionitinke sekunde. Ako bi test išao kroz bazu i mrežu, trajao bi stotine puta duže, i 500 testova bi trajalo minutama umjesto desetinke sekunde.popust, greška je u funkciji popust. Kad padne test kroz web stranicu, greška može biti bilo gdje.Zato je u DeliveryApp pravilo odvojeno od svega ostalog: funkcije cijena_dostave, popust i ukupno ne znaju ništa o bazi ni o ekranu; dobiju brojeve i vrate broj. Ovakav dizajn se zove testabilan: lako ga je testirati. Kad je kod pisan tako da pravilo, baza i ekran žive zajedno u jednoj funkciji od 200 redova, jedinično testiranje postaje teško, i to je znak lošeg dizajna, a ne lošeg testera.
Slika iz života: kontrola kvaliteta u fabrici bicikala. Svaki dio (kočnica, lanac, točak) testira se na posebnom stolu, prije sklapanja. Ako kočnica ne valja, zna se odmah da je kočnica, a ne „nešto na biciklu”.
Skoro svaki dobar test ima tri dijela, uvijek istim redom:
Primjer: test da se narudžba u pripremi ne može otkazati (PP-6).
import pytest
from deliveryapp import Narudzba
def test_u_pripremi_se_ne_moze_otkazati():
# Pripremi: narudžba koju je restoran prihvatio i počeo pripremati
n = Narudzba("N-1001")
n.promijeni("PRIHVAĆENA")
n.promijeni("U_PRIPREMI")
# Izvrši i provjeri: otkazivanje mora javiti grešku
with pytest.raises(ValueError):
n.promijeni("OTKAZANA")
# Provjeri: stanje se nije promijenilo
assert n.stanje == "U_PRIPREMI"Prazni redovi između tri dijela nisu ukras. Oni pokazuju čitaocu gdje se šta dešava, i test se čita kao mala priča: „imamo narudžbu u pripremi; pokušamo je otkazati; to ne smije uspjeti, i ona ostaje u pripremi.”
Ovaj test, pokrenut nad prvom verzijom DeliveryApp (DeliveryApp=v0), pada. U prvoj verziji je dozvoljen prelaz iz U_PRIPREMI u OTKAZANA (defekt D-04), pa izuzetak ne bude javljen. pytest kaže:
FAILED tests/test_narudzba.py::test_u_pripremi_se_ne_moze_otkazati - Failed: DID NOT RAISE ValueError„DID NOT RAISE” znači: očekivali smo grešku, a nije je bilo. Defekt je nađen jednim testom od deset redova, za milisekundu.

Kad test padne na serveru za kontinuiranu integraciju u dva ujutro, programer ujutro vidi samo njegovo ime i poruku. Zato ime mora reći šta se provjerava i šta se očekuje:
| Loše ime | Dobro ime |
|---|---|
test1 | test_n1001_ukupno_24_50 |
test_dostava | test_dostava_besplatna_od_30_eura |
test_narudzba_radi | test_u_pripremi_se_ne_moze_otkazati |
test_popust_2 | test_kod_malim_slovima_daje_popust |
Drugo pravilo: jedan test provjerava jedno ponašanje. Test koji provjerava dostavu, popust, otkazivanje i ispis cijene u jednom ima dvije mane. Prvo, kad padne, ne zna se šta je pokvareno. Drugo, prvi assert koji padne zaustavi test, pa ostale provjere uopšte nisu izvršene, i ne znamo da li je i tamo nešto pokvareno.
To ne znači da test smije imati samo jedan assert. Test test_prihvacena_moze_u_pripremu provjerava i stanje i istoriju, jer su to dva dijela istog ponašanja („prelaz je zapisan”). Pravilo je jedno ponašanje, ne jedan red.
Treće pravilo: test ne sadrži logiku. U testu nema if, petlji koje računaju očekivani rezultat, ni kopirane formule iz koda. Očekivani rezultat je konkretan broj, izračunat rukom (test orakul iz poglavlja 1). Test koji računa ocekivano = 200 + 50 * math.ceil(km - 3) samo ponavlja kod, pa ako je formula pogrešna, pogrešni su i kod i test, a test prolazi.
„Testove ne treba pisati uredno, to je samo pomoćni kod.” Testovi se čitaju češće od koda: svaki put kad padnu i svaki put kad neko želi saznati šta kod treba raditi. Dobri testovi su živa dokumentacija: iz imena testova DeliveryApp može se pročitati cijeli spisak pravila.
Odgovori: tipka R ili klik na zamućeno polje.
Granice dostave traže mnogo sličnih testova: 0 km, 3 km, 3,01 km, 4 km, 4,2 km, 10 km… Osam funkcija koje se razlikuju samo u dva broja je dosadno pisati i lako je pogriješiti pri kopiranju. pytest zato ima parametrizaciju: jedan test, izvršen za svaki red tabele.
import pytest
from deliveryapp import cijena_dostave
@pytest.mark.parametrize("km, ocekivano", [
(0, 200), (3, 200), (3.01, 250), (3.1, 250),
(4, 250), (4.2, 300), (9.99, 550), (10, 550),
])
def test_dostava_po_km(km, ocekivano):
assert cijena_dostave(km, 1000) == ocekivanoOznaka @pytest.mark.parametrize dobije imena parametara ("km, ocekivano") i listu redova. pytest izvrši test osam puta, svaki put s drugim redom, i svaki red se u izvještaju vidi kao poseban test:
test_granice.py::test_dostava_po_km[0-200] PASSED
test_granice.py::test_dostava_po_km[3-200] PASSED
test_granice.py::test_dostava_po_km[3.01-250] PASSED
...Kad padne jedan red, ostali se i dalje izvrše, i u izvještaju se tačno vidi koji red je pao. Parametrizovan test je, u stvari, tabela očekivanih rezultata iz poglavlja 1 pretvorena u kod: dodavanje novog slučaja je dodavanje jednog reda.
Neki ulazi nisu dozvoljeni: 11 km je izvan zone (PP-3), 4,99 € je ispod minimuma (PP-1). Funkcija tada ne vraća broj, nego javlja izuzetak (raise ValueError(...)). I to je ponašanje koje treba testirati. U pytestu se koristi pytest.raises:
@pytest.mark.parametrize("km", [-0.01, 10.01, 11])
def test_izvan_zone(km):
with pytest.raises(ValueError):
cijena_dostave(km, 1000)Blok with pytest.raises(ValueError): kaže: „kod unutar ovog bloka mora javiti ValueError”. Ako ga javi, test prolazi. Ako ga ne javi, test pada s porukom DID NOT RAISE (kao u 5.2). Može se provjeriti i tekst poruke, s match: pytest.raises(ValueError, match="izvan zone").
Testovi izuzetaka se često zaboravljaju, a baš oni hvataju opasne defekte: funkcija koja za 11 km tiho vrati 600 centi umjesto da odbije narudžbu šalje dostavljača izvan grada.
Zašto DeliveryApp računa u centima? U pytestu postoji i pytest.approx za poređenje decimalnih brojeva, i treba ga poznavati. Probajte u Pythonu:
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
>>> round(2.675, 2)
2.67Računar decimalne brojeve čuva u binarnom obliku, i većina brojeva kao 0,1 ne može se zapisati tačno, isto kao što se 1/3 ne može tačno zapisati decimalno (0,333…). Rezultat je malo pogrešan. Za fiziku je to u redu, i tada se koristi assert x == pytest.approx(0.3), koje dozvoljava sitnu razliku. Ali za novac to nije u redu: kupac ne smije platiti 24,499999 €. Zato DeliveryApp sve iznose čuva kao cijele brojeve centi (24,50 € = 2450), a decimalni zarez se pojavljuje samo pri ispisu na ekran (funkcija eur). Cijeli brojevi se u Pythonu računaju tačno, pa se testovi novca pišu s običnim ==.
Python ima i modul decimal za tačan decimalni račun, koji koriste banke i knjigovodstveni programi. Cijeli centi su jednostavniji i dovoljni za DeliveryApp.
Testovi klase Narudzba često trebaju isto početno stanje, npr. prihvaćenu narudžbu. Umjesto da svaki test ponavlja dva reda pripreme, pytest ima fixture: funkciju koja pripremi objekat i preda ga testu.
import pytest
from deliveryapp import Narudzba
@pytest.fixture
def prihvacena():
n = Narudzba("N-1001")
n.promijeni("PRIHVAĆENA")
return n
def test_prihvacena_moze_u_pripremu(prihvacena):
prihvacena.promijeni("U_PRIPREMI")
assert prihvacena.stanje == "U_PRIPREMI"
assert prihvacena.istorija == ["NOVA", "PRIHVAĆENA", "U_PRIPREMI"]
def test_prihvacena_se_moze_otkazati(prihvacena):
assert prihvacena.moze_otkazati()Kako pytest zna šta da preda? Po imenu: test ima parametar prihvacena, a postoji fixture istog imena, pa ga pytest pozove i rezultat preda testu. Važno: fixture se poziva za svaki test iznova, pa svaki test dobija svoju, novu narudžbu. Kad jedan test promijeni stanje u U_PRIPREMI, drugi test i dalje dobija prihvaćenu narudžbu. To je slovo I iz FIRST (nezavisnost, poglavlje 4).
Fixture se mogu staviti u fajl conftest.py i tada ih vide svi testovi u folderu. U DeliveryApp conftest.py već bira verziju koda (ispravnu ili v0). Fixture mogu i počistiti poslije testa (npr. obrisati privremenu bazu), o čemu više u poglavlju 9.
Odgovori: tipka R ili klik na zamućeno polje.
Do sada smo testove pisali poslije koda. Razvoj vođen testovima (TDD), koji je Kent Beck opisao u knjizi iz 2002. godine, okreće redoslijed. Radi se u kratkom ciklusu od tri koraka, koji traje nekoliko minuta:
Pa ispočetka, sa sljedećim malim testom.
Zašto bi neko radio ovako? Četiri koristi:
TDD nije uvijek najbolji izbor (za istraživanje nove ideje ili za izgled ekrana manje pomaže), ali za pravila poput onih u DeliveryApp je odličan.

Udruženje restorana traži novo pravilo:
PP-8. Kupac može ostaviti napojnicu dostavljaču od 0, 5, 10 ili 15 % iznosa artikala. Napojnica se zaokružuje naviše na 10 centi i iznosi najviše 10,00 €.
(Recenzija iz poglavlja 2 je pomogla: ovaj put pravilo kaže kako se zaokružuje.) Razvijamo funkciju napojnica(iznos, procenat) postupkom TDD.
Krug 1 – crveno. Najjednostavniji slučaj: bez napojnice.
from napojnica import napojnica
def test_bez_napojnice():
assert napojnica(2150, 0) == 0Pokrenemo: pada, jer modul napojnica ne postoji (ModuleNotFoundError, u izvještaju E). Crveno.
Krug 1 – zeleno. Najmanje koda:
def napojnica(iznos, procenat):
return 0Zeleno. Da, ovo izgleda smiješno, ali je namjerno: test ne traži više, pa ne pišemo više.
Krug 2 – crveno. Pravi slučaj: 10 % na N-1001. Rukom: 10 % od 21,50 € je 2,15 €, naviše na 10 centi je 2,20 €.
def test_deset_posto_n1001():
assert napojnica(2150, 10) == 220Pada: assert 0 == 220. Crveno.
Krug 2 – zeleno, prvi pokušaj. Napišemo return iznos * procenat // 100. Pada: assert 215 == 220. Zaboravili smo zaokruživanje na 10 centi! Test nas je upozorio u sekundi. Drugi pokušaj, sa zaokruživanjem naviše:
def napojnica(iznos, procenat):
centi = -(-iznos * procenat // 100) # dijeljenje zaokruženo naviše
return -(-centi // 10) * 10 # naviše na 10 centiZeleno, oba testa. (Trik -(-a // b) daje dijeljenje cijelih brojeva zaokruženo naviše, bez decimalnih brojeva; // sam zaokružuje naniže.)
Krug 3 – crveno. Granica od 10,00 €: 15 % od 90,00 € je 13,50 €, a smije biti najviše 10,00 €.
@pytest.mark.parametrize("iznos, procenat, ocekivano", [
(2000, 5, 100), # tačno 1,00 €, nema zaokruživanja
(2010, 5, 110), # 1,005 € → 1,01 € → 1,10 €
(3200, 15, 480), # N-1002: 4,80 €
(6660, 15, 1000), # 9,99 € → 10,00 € (granica)
(9000, 15, 1000), # 13,50 € → najviše 10,00 €
])
def test_zaokruzivanje_i_granica(iznos, procenat, ocekivano):
assert napojnica(iznos, procenat) == ocekivanoČetiri reda prolaze, posljednji pada: assert 1350 == 1000. Dodamo min(..., 1000). Zeleno.
Krug 4 – crveno. Nedozvoljen procenat (3 %, 20 %, −5 %) mora biti odbijen:
@pytest.mark.parametrize("procenat", [-5, 3, 20, 100])
def test_nedozvoljen_procenat(procenat):
with pytest.raises(ValueError, match="0, 5, 10 ili 15"):
napojnica(2150, procenat)Pada: DID NOT RAISE. Dodamo provjeru. Zeleno.
Refaktorisanje. Svi testovi su zeleni, pa uređujemo: brojevi postaju konstante s imenima, dodajemo opis funkcije. Konačna verzija (primjeri/deliveryapp/napojnica.py):
DOZVOLJENI = {0, 5, 10, 15}
MAX_NAPOJNICA = 1000 # 10,00 €
def napojnica(iznos, procenat):
"""Napojnica u centima za iznos artikala u centima."""
if procenat not in DOZVOLJENI:
raise ValueError("napojnica može biti 0, 5, 10 ili 15 %")
centi = -(-iznos * procenat // 100) # dijeljenje zaokruženo naviše
na_10 = -(-centi // 10) * 10 # naviše na 10 centi
return min(na_10, MAX_NAPOJNICA)Pokrenemo sve testove ponovo: 11 prolaza. Funkcija je nastala za četiri kruga, svaki red ima test, a jednu grešku (zaboravljeno zaokruživanje) test je uhvatio prije nego što je kod iko vidio.
Predavač radi TDD uživo, a studenti diktiraju sljedeći test. Pravilo igre: niko ne smije napisati red koda dok ne postoji test koji pada.
Jedinični testovi su temelj piramide, ali nisu cijela piramida. Oni ne vide:
napojnica je ispravna, ali da li je API zaista poziva i da li ekran prikazuje rezultat? (integracioni testovi, poglavlje 9);Ponekad jedinica zavisi od nečega što u testu ne želimo pozvati: servis za plaćanje, servis mapa, trenutno vrijeme. Tada se taj dio zamijeni test dvojnikom: lažnim objektom koji glumi pravi i vraća ono što test treba. Na primjer, funkcija koja računa udaljenost preko servisa mapa u testu dobija lažni servis koji uvijek vrati 4,2 km. Test dvojnike (lažnjake, stubove i mockove) detaljno učimo u poglavlju 9.
Mnogi timovi mjere da je većina njihovih defekata „na šavovima”: između dijelova koji su svaki za sebe bili dobro testirani. Heathrow 2008. iz poglavlja 2 je veliki primjer. Zato iznad jediničnih testova uvijek treba bar nekoliko integracionih i sistemskih.
Odgovori: tipka R ili klik na zamućeno polje.
Cilj je razviti dvije nove funkcije postupkom TDD i napisati jedinične testove klase Narudzba. Radi se u parovima, po modelu „ping-pong”: jedan piše test koji pada, drugi kod koji ga zadovolji, pa zamjena.
napojnica.py i tests/test_napojnica.py.pytest -v tests/test_napojnica.py: mora biti 11 prolaza. Uporedite s primjeri/deliveryapp/napojnica.py.Pravilo za TDD:
PP-9. Procijenjeno vrijeme dostave u minutama je: vrijeme pripreme restorana + 4 minute po započetom kilometru, zaokruženo naviše na 5 minuta. Vrijeme pripreme je 20 minuta za R-1, 15 za R-2 i 10 za R-3. Za nepoznat restoran funkcija javlja ValueError.
vrijeme_dostave(restoran, km) u krugovima crveno–zeleno–refaktoriši. Svaki krug jedan commit.tests/test_moja_narudzba.py s fixtureom prihvacena (5.6) i još jednim fixtureom na_putu (narudžba prošla NOVA → PRIHVAĆENA → U_PRIPREMI → NA_PUTU).pytest.raises) i test istorije.DeliveryApp=v0). Koji test hvata D-04?U Python konzoli probajte 0.1 + 0.2, round(2.675, 2) i sum([0.1] * 10). Zapišite rezultate. Zatim napišite funkciju ukupno_eur(stavke) koja sabira cijene u eurima kao decimalne brojeve (9.5, 2.5, 0.1 …) i nađite listu stavki za koju zbir nije tačan. Objasnite zašto DeliveryApp koristi cente.
Zatražite od AI asistenta da napiše funkciju vrijeme_dostave i testove za nju odjednom. Pokrenite te testove nad vašom implementacijom i vaše testove nad AI implementacijom. Da li se slažu? Ako ne, ko je u pravu po pravilu PP-9? Zapišite primjer u kojem je AI napisao test koji samo ponavlja svoju formulu (logika u testu, 5.3).
Rezultat koji se predaje: repozitorij s napojnica.py, vrijeme_dostave.py i testovima, istorija commitova koja pokazuje cikluse TDD-a, tests/test_moja_narudzba.py s fixtureima, i fajl rezultati-5.md s tabelom PP-9, nalazima recenzije, rezultatima nad v0 i primjerima iz koraka 4.
Rješenja su sakrivena – tipka R ili klik na okvir.
Označite tri dijela u testu i recite šta nije u redu:
def test_narudzba():
n = Narudzba("N-1005")
n.promijeni("PRIHVAĆENA")
assert n.stanje == "PRIHVAĆENA"
n.promijeni("U_PRIPREMI")
assert n.stanje == "U_PRIPREMI"
n.promijeni("NA_PUTU")
n.promijeni("ISPORUČENA")
assert n.istorija[-1] == "ISPORUČENA"Pripremi je prvi red; ostatak je naizmjenično izvrši i provjeri. Test provjerava četiri prelaza u jednom, pa kad padne, ne zna se koji prelaz je pokvaren, a ime test_narudzba ne kaže ništa. Bolje: odvojeni testovi s imenima kao test_nova_moze_biti_prihvacena, ili jedan test test_puni_put_do_isporuke koji provjerava cijelu istoriju na kraju.
Napišite parametrizovan test za popust: 21,50 € → 2,15 €; 49,90 € → 4,99 €; 50,00 € → 5,00 €; 80,00 € → 5,00 €; nepoznat kod STUDENT20 na 21,50 € → 0,00 €.
@pytest.mark.parametrize("iznos, kod, ocekivano", [
(2150, "STUDENT10", 215), (4990, "STUDENT10", 499), (5000, "STUDENT10", 500),
(8000, "STUDENT10", 500), (2150, "STUDENT20", 0),
])
def test_popust(iznos, kod, ocekivano):
assert popust(iznos, kod) == ocekivanoNapišite test koji provjerava da je narudžba od 4,99 € na 2 km odbijena s porukom koja sadrži riječ „minimalne”.
def test_ispod_minimuma_odbijeno():
with pytest.raises(ValueError, match="minimalne"):
cijena_dostave(2, 499)(Poruka u kodu je „iznos manji od minimalne narudžbe”.)
Izračunajte napojnicu po PP-8: a) 5 % na 24,50 €; b) 15 % na 32,00 €; c) 10 % na 99,99 €; d) 15 % na 70,00 €.
a) 1,225 € → 123 centa → naviše na 10: 1,30 €. b) 4,80 € → 4,80 €. c) 9,999 € → 1000 centi → 10,00 €. d) 10,50 € → najviše 10,00 €.
Student je napisao cijelu funkciju vrijeme_dostave, pa onda pet testova, i svi su odmah prošli. Je li to TDD? Šta je rizik?
Nije: testovi su napisani poslije koda i nijedan nije viđen da pada. Rizik je da neki test ne provjerava ono što misli (npr. pogrešan parametar, ili očekivani rezultat prepisan iz koda), a mi to ne znamo, jer test koji nikad nije pao možda ne provjerava ništa. Brza provjera: namjerno pokvariti kod i vidjeti da li testovi padnu (ideja testiranja mutacijama, poglavlje 7).