Dio 1 – Klase i granice
Dio 2 – Pravila i stanja
Dio 3 – Kombinacije i iskustvo
Praksa – Lov na greške D-01 … D-05
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 8 (Sedmica 8, 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 julu 2012. južnokorejski pjevač PSY objavio je na YouTubeu spot za pjesmu „Gangnam Style”. Ples u kojem se „jaše” nevidljivi konj obišao je svijet, i spot je postao prvi video s milijardu pregleda. Ali brojanje se nastavilo. Početkom decembra 2014. YouTube je na svojoj stranici na Google+ objavio neobičnu vijest: „Nikad nismo mislili da će neki video biti pogledan više puta nego što stane u 32-bitni cijeli broj (2 147 483 647 pregleda), ali to je bilo prije nego što smo upoznali PSY-ja.” Brojač pregleda bio je napravljen kao 32-bitni cijeli broj s predznakom, a najveći takav broj je 2³¹ − 1 = 2 147 483 647. Šta se desi kad mu se doda još jedan? U mnogim programima broj se „prelije” i postane najmanji mogući: −2 147 483 648.
Gangnam Style bi tada pokazivao minus dvije milijarde pregleda. Google je kasnije rekao da su inženjeri problem vidjeli nekoliko mjeseci ranije i na vrijeme prebacili brojač na 64-bitni broj, koji ide do oko 9,2 triliona. Priča je postala šala na internetu, ali iza nje stoji ozbiljna pouka za testere: greške žive na granicama. Niko ne bi testirao brojač s 1 000 ili 1 000 000 pregleda i našao problem. Problem je bio tačno na jednom broju: 2 147 483 647 + 1. Ovo poglavlje uči kako se takvi brojevi nalaze iz specifikacije, prije nego što ih nađe svijet.

U poglavljima 6 i 7 gledali smo kod iznutra i vidjeli njegovu slabost: testovi koji obilaze sve puteve ne stoje na granicama, pa greška D-01 preživi 100 % pokrivenosti, a pet od dvanaest mutanata preživi testove. Sada zatvaramo haubu i vraćamo se crnoj kutiji: testove pravimo samo iz pravila PP-1 … PP-6, ne gledajući kod. Prvo učimo klase ekvivalencije i granične vrijednosti, najkorisniji par tehnika u cijelom testiranju. Zatim tabelu odlučivanja za pravila koja zavise od više uslova i prelaze stanja za narudžbu. Na kraju kombinatorno testiranje po parovima i pogađanje grešaka. U praktičnoj nastavi konačno otkrivamo pet grešaka ubačenih u prvu verziju DeliveryApp: D-01 do D-05.
Nakon poglavlja možete:
podijeliti ulaz na važeće i nevažeće klase ekvivalencije; za svaku granicu izabrati granične vrijednosti po pravilu dvije ili tri vrijednosti; objasniti zašto su greške na granicama tako česte; napraviti tabelu odlučivanja za pravilo s više uslova i iz nje testove; nacrtati dijagram stanja i napisati testove svih dozvoljenih i nedozvoljenih prelaza; izračunati koliko kombinacija treba za puno i za testiranje po parovima; napraviti listu za pogađanje grešaka; za svaki pad testa nad prvom verzijom DeliveryApp reći koja je greška i koja tehnika ju je našla.
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 8.1–8.3 Crna kutija; klase ekvivalencije; granične vrijednosti | Gdje bi vi sakrili grešku? |
| Teorija 2 | 8.4–8.5 Tabela odlučivanja; prelazi stanja | 49 parova stanja za jedan test |
| Teorija 3 | 8.6–8.8 Testiranje po parovima; pogađanje grešaka; kako izabrati tehniku | 72 kombinacije u 9 testova |
Za crnu kutiju nam treba samo jedno: dobra specifikacija. Za DeliveryApp to su pravila iz poglavlja 1:
| Oznaka | Pravilo |
|---|---|
| PP-1 | Najmanja narudžba je 5,00 € (zbir artikala); manje se odbija. |
| PP-2 | Dostava je 2,00 € do 3 km, plus 0,50 € za svaki započeti kilometar preko 3 km. |
| PP-3 | Dostava ide od 0 do najviše 10 km; negativna ili veća udaljenost se odbija. |
| PP-4 | Za artikle od 30,00 € i više dostava je besplatna. |
| PP-5 | Kod STUDENT10 (velika ili mala slova, razmaci na krajevima se zanemaruju) daje 10 % popusta na artikle, najviše 5,00 €. |
| PP-6 | Kupac smije otkazati narudžbu u stanju NOVA ili PRIHVAĆENA; od stanja U_PRIPREMI više ne. |
Problem je u tome što ulaza ima beskonačno mnogo. Udaljenost može biti 0, 0,1, 0,11, 2,5, 3, 3,0001… Iznos može biti bilo koji broj centi. Ne možemo testirati sve (prvi princip iz poglavlja 1: iscrpno testiranje je nemoguće). Treba nam pametan izbor: što manje testova koji hvataju što više grešaka. Tehnike crne kutije su upravo recepti za taj izbor.
Crna kutija ima i jednu veliku prednost nad bijelom: testove možemo napisati prije nego što kod postoji, čim su pravila jasna. Tako je napravljen tests/test_granice.py iz poglavlja 4: iz pravila, a ne iz koda. Sad ćemo vidjeti kako je nastao svaki njegov red.
tests/test_granice.py napravljena je iz pravila PP-1 … PP-6.Ideja je jednostavna: ako sistem za dva ulaza radi po istom pravilu, dovoljno je testirati jedan od njih. Pažljivo s riječju „isto”: 4 km i 4,5 km ne daju istu cijenu (4 km je jedan dodatni kilometar, a 4,5 km su dva započeta), ali se računaju istom formulom. Zato: sve udaljenosti od 0 do 3 km daju istu cijenu (2,00 €), pa su jedna klasa. Udaljenosti preko 3 do 10 km se računaju istom formulom (2,00 € + 0,50 € po započetom km), pa su druga klasa, iako daju različite iznose.
Klasa ekvivalencije je skup ulaza za koje sistem treba da radi po istom pravilu. Iz svake klase uzmemo jednog predstavnika: ako radi za njega, vjerovatno radi za cijelu klasu; ako ne radi, vjerovatno ne radi za cijelu klasu.
Slika iz života: kontrola kvaliteta jabuka. Ne probate svaku jabuku iz gajbe. Podijelite ih po sortama i iz svake sorte probate jednu.
Klase za ulaze DeliveryApp (slika 8.2):
| Ulaz | Važeće klase | Nevažeće klase |
|---|---|---|
| udaljenost (PP-2, PP-3) | V1: 0 – 3 km (2,00 €); V2: preko 3 do 10 km (+0,50 € po započetom km) | N1: manje od 0; N2: više od 10 |
| iznos artikala (PP-1, PP-4) | V3: 5,00 – 29,99 € (plaća dostavu); V4: 30,00 € i više (besplatna) | N3: manje od 5,00 € |
| kod popusta (PP-5) | V5: STUDENT10 (bilo kako napisan); V6: bez koda | N4: nepoznat kod (prihvata se, ali bez popusta) |
| popust (PP-5) | V7: 10 % manje od 5,00 € (iznos do 50,00 €); V8: 10 % više od 5,00 € (popust je 5,00 €) | – |

Iz klasa se testovi prave po dva pravila:
Najmanji skup testova koji pokriva sve klase:
| Test | Ulaz | Klase | Očekivano |
|---|---|---|---|
| E-1 | 2,5 km, 10,00 €, bez koda | V1, V3, V6 | dostava 2,00 € |
| E-2 | 4,2 km, 21,50 €, STUDENT10 | V2, V3, V5, V7 | 22,35 € (N-1001) |
| E-3 | 2,5 km, 80,00 €, STUDENT10 | V1, V4, V5, V8 | 75,00 € (popust 5,00 €, dostava 0) |
| E-4 | −1 km, 10,00 € | N1 | odbijeno: izvan zone |
| E-5 | 11 km, 10,00 € | N2 | odbijeno: izvan zone |
| E-6 | 2 km, 4,50 € | N3 | odbijeno: ispod minimuma |
| E-7 | 4,2 km, 21,50 €, STUDENT20 | N4 | 24,50 € (bez popusta) |
Sedam testova pokriva sve klase. Ali pazite: nijedan nije na granici. Za D-01 (> umjesto >= kod 30,00 €) ovih sedam testova je slijepo, kao i testovi iz poglavlja 6.
„Ulaz STUDENT10 napisan malim slovima je ista klasa kao velikim.” Po pravilu PP-5 jeste, jer sistem treba da ga prihvati isto. Ali programer to možda nije znao. Kad pravilo kaže „velika ili mala slova se zanemaruju”, to je znak da u klasi V5 treba testirati više predstavnika: STUDENT10, student10 i STUDENT10 s razmacima. Klase su alat za razmišljanje, a ne zakon.
Gangnam Style, D-01, mutanti M1, M2, M5 i M6 iz poglavlja 7: sve su to greške na ivici. Zašto su granice tako opasne?
< i <=, > i >=. To je pola sekunde razmišljanja i lako se pogriješi.Analiza graničnih vrijednosti kaže: za svaku granicu između klasa testiraj vrijednosti na granici i odmah do nje. Postoje dvije varijante:
== umjesto <=), ali traži više testova.Šta je „odmah do” granice? Za cijene u centima to je 1 cent: 29,99 € i 30,00 €. Za udaljenost zavisi od toga s kojom tačnošću aplikacija mjeri; uzmimo 0,01 km (10 metara).
Granice DeliveryApp i testovi iz tests/test_granice.py:
| Granica | Pravilo | Vrijednosti | Očekivano |
|---|---|---|---|
| 0 km | PP-3 | −0,01 km / 0 km | odbijeno / 2,00 € |
| 3 km | PP-2 | 3 km / 3,01 km | 2,00 € / 2,50 € |
| 10 km | PP-3 | 10 km / 10,01 km | 5,50 € / odbijeno |
| 5,00 € | PP-1 | 4,99 € / 5,00 € | odbijeno / dostava 3,00 € (na 5 km) |
| 30,00 € | PP-4 | 29,99 € / 30,00 € | 3,00 € / 0,00 € (na 5 km) |
| 50,00 € (popust) | PP-5 | 49,90 € / 50,00 € / 80,00 € | 4,99 € / 5,00 € / 5,00 € |
U kodu (iznos u centima, dostava za 10,00 € artikala ili na 5 km):
@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) == ocekivano
@pytest.mark.parametrize("iznos, ocekivano", [(500, 300), (2999, 300), (3000, 0), (3001, 0)])
def test_besplatna_dostava(iznos, ocekivano):
assert cijena_dostave(5, iznos) == ocekivano
@pytest.mark.parametrize("km", [-0.01, 10.01, 11])
def test_izvan_zone(km):
with pytest.raises(ValueError):
cijena_dostave(km, 1000)Pored čistih granica, test ima i nekoliko vrijednosti unutar klase V2 (3,1, 4, 4,2, 9,99 km). One provjeravaju „započeti kilometar”: 4 km je tačno jedan dodatni kilometar (2,50 €), a 4,2 km su dva započeta (3,00 €). Ovo je granica unutar formule, svaki cijeli kilometar je mala granica.
Brojač pregleda je 32-bitni cijeli broj s predznakom. Koje biste granične vrijednosti testirali? – 0 (prvi pregled), 2 147 483 646, 2 147 483 647 (najveći) i pokušaj da se doda još jedan. Očekivano: brojač ne smije postati negativan; ili se prijavi greška, ili se koristi veći tip.
Gangnam Style nije jedini. Avion Boeing 787 imao je, po upozorenju američke uprave za civilno vazduhoplovstvo iz 2015., brojač u upravljanju strujom koji bi se prelio poslije 248 dana neprekidnog rada; preporuka je bila da se sistem povremeno ponovo pokrene. 248 dana, kad se pretvori u stotinke sekunde, je baš oko 2³¹. Isti broj, druga priča.
< i <= i jer pravila često ne kažu jasno šta je s granicom.Odgovori: tipka R ili klik na zamućeno polje.
Klase i granice gledaju jedan ulaz po jedan. Ali neka pravila zavise od više uslova zajedno. Konačna cijena zavisi od iznosa (besplatna dostava?), koda (popust?) i od toga da li je 10 % veće od 5,00 € (gornja granica). Za takva pravila koristi se tabela odlučivanja: svaki uslov je red, svaka kombinacija je kolona, a ispod su ishodi.
Uslovi:
Tri uslova sa po dva ishoda daju 2³ = 8 kombinacija. Neke su nemoguće: ako iznos nije ni 30,00 €, ne može biti veći od 50,00 €. Neke su iste: bez koda je svejedno da li je iznos preko 50,00 €. Kad se to sredi, ostaje pet pravila:
| P1 | P2 | P3 | P4 | P5 | |
|---|---|---|---|---|---|
| U1: iznos ≥ 30,00 € | ne | ne | da | da | da |
| U2: važeći kod | ne | da | ne | da | da |
| U3: iznos > 50,00 € | – | (nemoguće da) | – | ne | da |
| Dostava | po km | po km | 0 | 0 | 0 |
| Popust | 0 | 10 % | 0 | 10 % | 5,00 € |
| Test | N-1001: 24,50 € | N-1001 + kod: 22,35 € | N-1002: 32,00 € | N-1002 + kod: 28,80 € | 10 × Ćevapi, 2,5 km, kod: 75,00 € |
Crtica „–” znači „svejedno”. Svaka kolona je jedan test, s očekivanim rezultatom izračunatim iz pravila:
Tabela odlučivanja ima još jednu korist: otkriva rupe u pravilima. Dok je pravite, morate odgovoriti na svaku kombinaciju. Šta ako je iznos 32,00 €, a s popustom padne ispod 30,00 €? Pravilo PP-5 na to odgovara (besplatna dostava gleda iznos prije popusta), ali da nije, tabela bi pokazala prazninu, i to prije pisanja koda.
Dodajte uslov U4: „kupac ostavlja napojnicu (PP-8)”. Koliko kolona tabela sada ima prije sređivanja? – 2⁴ = 16. Koliko testova biste stvarno napravili? Napojnica ne zavisi od dostave i popusta (računa se na iznos artikala), pa je dovoljno dodati jedan ili dva testa s napojnicom, a ne udvostručiti tabelu.
Narudžba nije samo broj: ona ima stanje koje se mijenja kroz vrijeme. Ista radnja („otkaži”) dozvoljena je u jednom stanju (NOVA), a zabranjena u drugom (U_PRIPREMI). Za takve sisteme koristi se testiranje prelaza stanja, iz dijagrama stanja (slika 8.3).

stateDiagram-v2
direction LR
[*] --> NOVA
NOVA --> PRIHVAĆENA: restoran prihvati
NOVA --> ODBIJENA: restoran odbije
NOVA --> OTKAZANA: kupac otkaže
PRIHVAĆENA --> U_PRIPREMI: počne priprema
PRIHVAĆENA --> OTKAZANA: kupac otkaže
U_PRIPREMI --> NA_PUTU: dostavljač preuzme
NA_PUTU --> ISPORUČENA: kupac primiIz dijagrama se prave dvije vrste testova:
Druga vrsta je često važnija. Programeri obično dobro naprave ono što treba da radi, a zaborave zabranu. Pravilo PP-6 je upravo zabrana.
Da li treba ručno napisati 49 testova? Ne: tabela parova se napravi programom, a pytest ih parametrizuje. Fajl tests/test_prelazi.py:
STANJA = ["NOVA", "PRIHVAĆENA", "ODBIJENA", "U_PRIPREMI", "NA_PUTU", "ISPORUČENA", "OTKAZANA"]
# Sedam dozvoljenih prelaza iz specifikacije (SADRZAJ.md), a ne iz koda
DOZVOLJENI = {
("NOVA", "PRIHVAĆENA"), ("NOVA", "ODBIJENA"), ("NOVA", "OTKAZANA"),
("PRIHVAĆENA", "U_PRIPREMI"), ("PRIHVAĆENA", "OTKAZANA"),
("U_PRIPREMI", "NA_PUTU"), ("NA_PUTU", "ISPORUČENA"),
}
@pytest.mark.parametrize("staro, novo", sorted(DOZVOLJENI))
def test_dozvoljen_prelaz(staro, novo):
n = narudzba_u_stanju(staro)
n.promijeni(novo)
assert n.stanje == novo
@pytest.mark.parametrize("staro, novo", [p for p in itertools.product(STANJA, STANJA) if p not in DOZVOLJENI])
def test_nedozvoljen_prelaz(staro, novo):
n = narudzba_u_stanju(staro)
with pytest.raises(ValueError):
n.promijeni(novo)
assert n.stanje == staroPomoćna funkcija narudzba_u_stanju dovede novu narudžbu u traženo stanje dozvoljenim putem (npr. za NA_PUTU: PRIHVAĆENA → U_PRIPREMI → NA_PUTU). Važno: skup DOZVOLJENI je prepisan iz specifikacije, a ne uvezen iz koda (PRELAZI u deliveryapp.py). Da smo ga uvezli iz koda, test bi provjeravao kod samim sobom i nikad ne bi pao.
pytest -q tests/test_prelazi.py
................................................. [100%]
49 passed in 0.04sJedan parametrizovan test, 49 slučajeva, 0,04 sekunde.
Jača varijanta je pokrivenost parova prelaza (1-switch coverage): svaki niz od dva uzastopna prelaza, npr. NOVA → PRIHVAĆENA → OTKAZANA. Hvata greške u kojima stanje „pamti” nešto iz prethodnog koraka. Za sistem s malo stanja kao naša narudžba dovoljno je ono što smo uradili.
Odgovori: tipka R ili klik na zamućeno polje.
Zamislimo da web forma DeliveryApp ima pet izbora:
| Parametar | Vrijednosti | Broj |
|---|---|---|
| restoran | R-1, R-2, R-3 | 3 |
| kod | bez koda, STUDENT10, nepoznat | 3 |
| udaljenost | do 3 km, 3–10 km | 2 |
| iznos | 5–29,99 €, od 30 € | 2 |
| plaćanje | kartica, gotovina | 2 |
Sve kombinacije: 3 × 3 × 2 × 2 × 2 = 72 testa. Uz deset parametara po pet vrijednosti bilo bi 5¹⁰, skoro deset miliona. Previše.
Istraživanja grešaka u stvarnim sistemima pokazala su nešto korisno: većinu grešaka izaziva jedan parametar sam ili par parametara zajedno (npr. „STUDENT10 ne radi kad je plaćanje gotovinom”). Greške koje traže tačnu kombinaciju tri, četiri ili pet parametara su rjeđe. Zato testiranje po parovima traži samo da se svaki par vrijednosti bilo koja dva parametra pojavi bar jednom.
Skripta primjeri/kombinacije/parovi.py bira kombinacije pohlepno: u svakom koraku uzme onu koja pokriva najviše još nepokrivenih parova.
python parovi.py
Sve kombinacije: 72; po parovima: 9| # | restoran | kod | udaljenost | iznos | plaćanje |
|---|---|---|---|---|---|
| K-01 | R-1 | bez koda | do 3 km | 5–29,99 € | kartica |
| K-02 | R-1 | STUDENT10 | 3–10 km | od 30 € | gotovina |
| K-03 | R-2 | nepoznat | do 3 km | 5–29,99 € | gotovina |
| K-04 | R-3 | nepoznat | 3–10 km | od 30 € | kartica |
| K-05 | R-2 | bez koda | 3–10 km | od 30 € | kartica |
| K-06 | R-3 | STUDENT10 | do 3 km | 5–29,99 € | kartica |
| K-07 | R-3 | bez koda | do 3 km | od 30 € | gotovina |
| K-08 | R-1 | nepoznat | 3–10 km | 5–29,99 € | kartica |
| K-09 | R-2 | STUDENT10 | do 3 km | 5–29,99 € | kartica |
Devet umjesto 72. I manje od devet ne može: restoran i kod imaju po tri vrijednosti, pa samo njihovih parova ima 3 × 3 = 9, a svaki test pokrije tačno jedan od njih. Provjerite sami u tabeli: R-2 i gotovina (K-03), STUDENT10 i od 30 € (K-02), nepoznat kod i 3–10 km (K-04, K-08)…
Tabela daje kombinacije, a ne gotove testove. Svaki red još treba pretvoriti u konkretan ulaz (npr. K-02: R-1, 2 × Capricciosa + 4 × Sok = 32,00 €, 4,2 km, STUDENT10, gotovina) i izračunati očekivani rezultat iz pravila (32,00 € − 3,20 € + 0,00 € = 28,80 €).
Za veće slučajeve postoje gotovi alati: biblioteka allpairspy za Python, Microsoftov PICT, i mnogi online generatori.
„Po parovima znači da su sve kombinacije pokrivene.” Ne: pokriveni su svi parovi. Greška koja se javlja samo za R-3, STUDENT10 i gotovinu zajedno može proći, jer ta trojka nije u tabeli. Testiranje po parovima je pametan kompromis, a ne garancija.
Posljednja tehnika nema formulu. Pogađanje grešaka koristi iskustvo: „na ovakvim mjestima programi često griješe”. To je ista ideja kao heuristike iz poglavlja 3, sad pretvorena u listu za automatske testove. Lista za DeliveryApp:
| Kategorija | Ulaz za probu | Šta očekujemo (pravilo) |
|---|---|---|
| prazno | kod "", kod None, prazna korpa | bez popusta; prazna korpa odbijena (PP-1) |
| razmaci i slova | STUDENT10 , student10, Student10 | popust 10 % (PP-5) |
| slični, a pogrešni | STUDENT 10, STUDENT1O (slovo O) | bez popusta |
| nula | 0 km, količina 0 | 2,00 €; stavka s količinom 0 se ne računa |
| negativno | −1 km, količina −1 | odbijeno (PP-3); količina ne smije biti negativna |
| ogromno | 1 000 000 €, 100 000 stavki | popust 5,00 €; sistem ne pada |
| decimale | 3,0000001 km, 0.1 + 0.2 | 2,50 €; iznosi u centima (poglavlje 5) |
| tip | "4,2" kao tekst umjesto broja | jasna poruka, a ne pad |
| zaokruživanje | 21,45 € sa STUDENT10 | 2,15 € ili 2,14 €? (nalaz recenzije iz poglavlja 2) |
| redoslijed | otkazivanje dva puta zaredom | drugi put odbijeno |
Neki od ovih testova će otkriti stvari koje pravila ne kažu: šta je s količinom 0? Šta je s praznom korpom? To nisu uvijek greške u kodu, nego pitanja za vlasnika proizvoda. Zapišite ih kao u recenziji iz poglavlja 2.
Testeri godinama skupljaju liste „zločestih” ulaza. Jedna poznata, otvorena lista na GitHubu (Big List of Naughty Strings) ima stotine tekstova koji ruše programe: prazne nizove, emotikone, tekst zdesna nalijevo, SQL komande, vrlo duge riječi. Dobar tester ima svoju listu i dopunjuje je svakom novom greškom koju vidi.
Tehnike se ne biraju „jedna ili druga”, nego zajedno, prema vrsti pravila:
| Kad pravilo ima… | Tehnika | Primjer u DeliveryApp |
|---|---|---|
| raspone brojeva | klase ekvivalencije + granične vrijednosti | udaljenost, iznos, popust |
| više uslova koji zajedno daju ishod | tabela odlučivanja | dostava + popust |
| stanja i radnje | prelazi stanja | narudžba, PP-6 |
| mnogo parametara s malo vrijednosti | po parovima | forma: restoran, kod, plaćanje |
| ulaze od korisnika | pogađanje grešaka | kod popusta, količina |
Redoslijed u praksi: prvo klase i granice (najviše grešaka po testu), zatim tabela i stanja za složenija pravila, i na kraju pogađanje kao dopuna. Poslije toga, alat za pokrivenost iz poglavlja 6 pokaže da li je neki dio koda ostao netaknut.
Odgovori: tipka R ili klik na zamućeno polje.
Od poglavlja 4 pokrećemo testove nad prvom verzijom DeliveryApp (deliveryapp_v0.py) i vidimo da padaju, ali nismo pitali zašto. Sad je vrijeme. Cilj je za svaki pad naći grešku u kodu, imenovati tehniku koja ju je našla i dopisati testove koji fale. Radi se u parovima.
tests/test_napojnica.py (poglavlje 5). Koja granica nedostaje?primjeri/deliveryapp pokrenite pytest -q tests/test_granice.py tests/test_narudzba.py tests/test_prelazi.py nad ispravnom verzijom: mora proći sve. Zatim $env:DeliveryApp="v0" (PowerShell) i ponovo. Zapišite koji testovi padaju (očekuje se 10 padova: 8 iz test_granice.py, 1 iz test_narudzba.py, 1 iz test_prelazi.py). Ostale testove u folderu upoznajemo u poglavlju 9.deliveryapp_v0.py i nađite red koji ga izaziva. Sada smijete gledati kod.math uvezen, a nekorišten). Koja greška objašnjava taj nalaz?tests/test_tabela.py sa pet testova iz tabele odlučivanja (P1 – P5 iz 8.4) preko funkcije ukupno. Koji od njih padaju na v0 i zbog koje greške?tests/test_parovi.py s bar četiri reda iz tabele po parovima (8.6) pretvorena u konkretne narudžbe s očekivanim iznosom. Parametar „plaćanje” DeliveryApp u kodu nema, pa ga zapišite samo kao komentar.rezultati-8.md umjesto testa.Otvorite web/index.html?v=0 (kao u poglavlju 3) i ručno ponovite tri testa koja su na v0 pala: 30,00 € na 5 km, kod student10, otkazivanje u pripremi. Da li stranica griješi isto kao Python kod?
Rezultat koji se predaje: fajl rezultati-8.md s tabelom klasa i granica za PP-8; tabela padova nad v0; spisak grešaka D-01 … D-05 s redom koda, opisom i tehnikom; tests/test_tabela.py, tests/test_parovi.py i testovi pogađanja; pitanja za vlasnika proizvoda; rezultat ručne provjere web stranice.
Rješenje koraka 3 – pet grešaka prve verzije. | Greška | Red u deliveryapp_v0.py | Padovi | Tehnika | | --- | --- | --- | --- | | D-01 iznos > BESPLATNA_OD umjesto >=: tačno 30,00 € plaća dostavu | if iznos > BESPLATNA_OD: | test_besplatna_dostava[3000-0]: 300 umjesto 0 | granična vrijednost | | D-02 round umjesto math.ceil: kilometri se zaokružuju na najbliži, a ne naviše | dodatni_km = round(max(0, udaljenost_km - 3)) | test_n1001 (2400 umjesto 2450), 3,01 km i 3,1 km (200 umjesto 250), 4,2 km (250 umjesto 300) | granična vrijednost, klasa V2 | | D-03 kod se ne pretvara u velika slova | KODOVI.get(kod.strip()) | test_kod_popusta[student10-215]: 0 umjesto 215 | klasa ekvivalencije (više predstavnika) | | D-04 otkazivanje dozvoljeno iz U_PRIPREMI | "U_PRIPREMI": {"NA_PUTU", "OTKAZANA"} | test_otkazivanje_u_pripremi, test_u_pripremi_se_ne_moze_otkazati, test_nedozvoljen_prelaz[U_PRIPREMI-OTKAZANA]: DID NOT RAISE | prelazi stanja | | D-05 nema gornje granice popusta | return round(iznos * procenat / 100) bez min | test_granica_popusta[8000-500]: 800 umjesto 500 | granična vrijednost, tabela odlučivanja (P5) | D-02 objašnjava nalaz alata ruff: prva verzija je uvezla math, ali umjesto math.ceil koristi ugrađeni round. Zanimljivo je da D-02 ne obara testove 4 km i 9,99 km: round(1) = 1 i round(6,99) = 7 daju isti rezultat kao ceil. Grešku otkrivaju samo vrijednosti malo iznad cijelog kilometra (3,01, 3,1, 4,2), što je još jedan dokaz da treba testirati odmah do granice.
Rješenja su sakrivena – tipka R ili klik na okvir.
Pravilo PP-9 (poglavlje 5): vrijeme dostave je vrijeme pripreme restorana (R-1 20, R-2 15, R-3 10 minuta) + 4 minute po započetom kilometru, zaokruženo naviše na 5 minuta; nepoznat restoran se odbija. Napravite klase ekvivalencije za ulaz „restoran” i po jedan test za svaku.
Važeće klase: R-1, R-2, R-3 (svaki je posebna klasa, jer ima drugo vrijeme pripreme). Nevažeća: bilo šta drugo („R-4”, „”, None). Testovi na 2 km (8 minuta vožnje): R-1 → 20 + 8 = 28 → 30 minuta; R-2 → 15 + 8 = 23 → 25; R-3 → 10 + 8 = 18 → 20; „R-4” → ValueError.
Za napojnicu PP-8 navedite granične vrijednosti za gornju granicu od 10,00 € pri 15 % i očekivane rezultate.
Bez zaokruživanja 10,00 € bi se dostiglo na 66,67 €. Ali zaokruživanje naviše na 10 centi pomjera granicu: 66,00 € → 9,90 € → 9,90 €; 66,01 € → 9,9015 € → 9,91 € → naviše na 10 centi → 10,00 €. Dakle prava granica je između 66,00 € i 66,01 €, a ne kod 66,67 €. Još jedan test iznad granice: 70,00 € → 10,50 € → najviše 10,00 €. (Provjereno funkcijom napojnica iz poglavlja 5: 990, 1000 i 1000 centi.) Pouka: granicu treba izračunati iz cijelog pravila, a ne samo iz jednog broja u njemu.
Restoran uvodi pravilo: „Vikendom je dostava besplatna za narudžbe od 20,00 € i više; radnim danom važi PP-4.” Napravite tabelu odlučivanja s uslovima vikend, iznos ≥ 20,00 €, iznos ≥ 30,00 €.
Moguće kombinacije (iznos ≥ 30 povlači iznos ≥ 20):
| P1 | P2 | P3 | P4 | P5 | P6 | |
|---|---|---|---|---|---|---|
| vikend | ne | ne | ne | da | da | da |
| iznos ≥ 20,00 € | ne | da | da | ne | da | da |
| iznos ≥ 30,00 € | ne | ne | da | ne | ne | da |
| dostava | po km | po km | 0 | po km | 0 | 0 |
Testovi na 5 km: P1 15,00 € radni dan → 3,00 €; P2 25,00 € radni dan → 3,00 €; P3 32,00 € radni dan → 0; P4 15,00 € vikend → 3,00 €; P5 25,00 € vikend → 0; P6 32,00 € vikend → 0. Kolona P5 je jedina u kojoj se novo pravilo razlikuje od starog. Uz to treba granica 19,99 € / 20,00 € vikendom.
Dodaje se stanje REKLAMACIJA: iz ISPORUČENA kupac može podnijeti reklamaciju, a iz REKLAMACIJA restoran je može riješiti (stanje RIJEŠENA). Koliko je sada dozvoljenih, a koliko nedozvoljenih parova?
Stanja: 9 (7 + REKLAMACIJA + RIJEŠENA), parova 9 × 9 = 81. Dozvoljenih: 7 + 2 = 9. Nedozvoljenih: 81 − 9 = 72. U test_prelazi.py treba samo dopuniti STANJA, PUT i DOZVOLJENI; parametrizacija sama napravi 81 slučaj.
Napišite pet ulaza za polje „udaljenost” na web formi na kojima biste očekivali grešku i šta pravilo kaže za svaki.
Npr.: 0 → 2,00 € (granica, PP-3); 4,2 s decimalnim zarezom → 3,00 € (forma treba prihvatiti zarez ili jasno reći da ne prihvata); prazno polje → poruka „unesite udaljenost”, a ne pad; -0 → 2,00 € ili odbijeno (pitanje za vlasnika proizvoda); 1e3 (naučni zapis, 1000 km) → odbijeno, izvan zone; deset → poruka o pogrešnom unosu.