Poglavlje 8: Tehnike crne kutije

Priča za početak. 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.

Slika 8.1 – Brojač od 32 bita: jedan pregled previše i broj postaje negativan

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.

Literatura. Osnovna literatura predmeta:

  1. J. Popović: Priručnik za test inženjere, CET i Računarski fakultet, Beograd, 2023.
  2. Rakić-Skoković: Priručnik za testiranje softvera, Fakultet tehničkih nauka, Novi Sad, 2013.
  3. G. J. Myers: The Art of Software Testing, 2. izdanje, John Wiley & Sons, 2004.
  4. A. P. Mathur: Foundations of Software Testing, Addison-Wesley Professional, 2008.

Za ovo poglavlje: L. Copeland: A Practitioner’s Guide to Software Test Design, Artech House, 2004; ISTQB Certified Tester Foundation Level Syllabus v4.0, 2023 (poglavlje o tehnikama crne kutije).

Riječi ovog poglavlja.

  • Crna kutija (black-box, specification-based) – testovi napravljeni iz zahtjeva i pravila, bez gledanja koda.
  • Klasa ekvivalencije (equivalence partition) – skup ulaza za koje sistem treba da se ponaša isto, pa je dovoljan jedan predstavnik.
  • Važeća i nevažeća klasa – ulazi koje sistem treba prihvatiti, odnosno odbiti.
  • Granična vrijednost (boundary value) – ulaz na samoj ivici klase, gdje se ponašanje mijenja (npr. tačno 3 km ili tačno 30,00 €).
  • Prelijevanje (overflow) – broj postane veći od najvećeg koji tip može čuvati, pa „preskoči” na pogrešnu vrijednost.
  • Tabela odlučivanja (decision table) – tabela koja za svaku kombinaciju uslova kaže koji je ishod.
  • Prelazi stanja (state transition testing) – testovi iz dijagrama stanja: dozvoljeni prelazi moraju raditi, nedozvoljeni moraju biti odbijeni.
  • Testiranje po parovima (pairwise testing) – izbor malog broja kombinacija tako da se svaki par vrijednosti dva parametra pojavi bar jednom.
  • Pogađanje grešaka (error guessing) – testovi iz iskustva: ulazi na kojima programi često griješe.
  • Pokrivenost klasa / granica – procenat klasa ili graničnih vrijednosti koje testovi provjeravaju.

Ciljevi poglavlja. 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.

Plan poglavlja – Sedmica 8: tri časa teorije i tri časa praktične nastave

Č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
Praksa 1–3 Lov na greške D-01 … D-05 u prvoj verziji DeliveryApp tests/test_granice.py, tests/test_prelazi.py

Dio 1 – Klase i granice

8.1 Testovi iz specifikacije

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.

Ukratko.

  • Crna kutija pravi testove samo iz specifikacije, ne gledajući kod.
  • Ulaza je beskonačno mnogo, pa tehnike crne kutije biraju malo testova koji hvataju mnogo grešaka.
  • Testovi crne kutije mogu se napisati prije koda, čim su pravila jasna.
  • Datoteka tests/test_granice.py napravljena je iz pravila PP-1 … PP-6.

8.2 Klase ekvivalencije

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 €) –

Slika 8.2 – Klase ekvivalencije i granice za udaljenost i iznos

Iz klasa se testovi prave po dva pravila:

  1. Važeće klase se mogu kombinovati: jedan test može pokriti više važećih klasa odjednom. N-1001 (4,2 km, 21,50 €, bez koda) pokriva V2, V3 i V6.
  2. Nevažeće klase se testiraju jedna po jedna: svaki test ima samo jedan nevažeći ulaz. Zašto? Ako test ima i −1 km i 4,50 €, a sistem ga odbije, ne znamo zbog čega: možda provjera udaljenosti ne radi, a odbijen je samo zbog iznosa.

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.

Česta zabluda. „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.

Ukratko.

  • Klasa ekvivalencije je skup ulaza za koje sistem radi po istom pravilu, pa je dovoljan jedan predstavnik.
  • Postoje važeće klase (sistem prihvata) i nevažeće klase (sistem odbija).
  • Važeće klase se smiju kombinovati u jednom testu, a nevažeće se testiraju jedna po jedna.
  • Sedam testova pokriva sve klase DeliveryApp, ali nijedan ne stoji na granici.

8.3 Granične vrijednosti

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?

  • Programer mora odlučiti između < i <=, > i >=. To je pola sekunde razmišljanja i lako se pogriješi.
  • Pravila često ne kažu jasno šta je s granicom: da li je „do 3 km” s 3 km ili bez? (PP-2 kaže „preko 3 km”, pa je 3 km još u osnovnoj cijeni.)
  • Brojanje „od nule” ili „od jedan” (kao petlja koja ide jedan korak previše).
  • Tipovi podataka imaju svoje granice (2 147 483 647 za 32 bita).

Analiza graničnih vrijednosti kaže: za svaku granicu između klasa testiraj vrijednosti na granici i odmah do nje. Postoje dvije varijante:

  • dvije vrijednosti: granica i prva vrijednost u susjednoj klasi (3 km i 3,01 km);
  • tri vrijednosti: vrijednost prije granice, granica i vrijednost poslije (2,99, 3 i 3,01 km). Ova varijanta hvata više grešaka (npr. == 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.

Na času. 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.

Iz stvarnog svijeta. 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.

Ukratko.

  • Greške se gomilaju na granicama jer se lako zamijene < i <= i jer pravila često ne kažu jasno šta je s granicom.
  • Analiza graničnih vrijednosti testira granicu i vrijednost odmah do nje, a u strožoj varijanti i vrijednost prije.
  • Za iznose u centima „odmah do” je jedan cent: 29,99 € i 30,00 €.
  • DeliveryApp ima granice 0 km, 3 km, 10 km, 5,00 €, 30,00 € i 50,00 € za gornju granicu popusta.

Provjeri se.

  1. Zašto se nevažeće klase testiraju jedna po jedna? – Da bi se znalo zbog kojeg ulaza je sistem odbio zahtjev.
  2. Koje vrijednosti testiraju granicu od 30,00 € po pravilu dvije vrijednosti? – 29,99 € (plaća dostavu) i 30,00 € (besplatna).
  3. Koji je najveći 32-bitni cijeli broj s predznakom? – 2 147 483 647, odnosno 2³¹ − 1.

Dio 2 – Pravila i stanja

8.4 Tabela odlučivanja

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:

  • U1: iznos artikala ≥ 30,00 €?
  • U2: važeći kod STUDENT10?
  • U3: 10 % iznosa > 5,00 € (odnosno iznos > 50,00 €)?

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:

  • P4: 32,00 € − 3,20 € (10 %) + 0,00 € = 28,80 €. Primijetite: besplatna dostava gleda iznos prije popusta (PP-5), iako je poslije popusta 28,80 € manje od 30,00 €. Ova kolona provjerava baš tu rečenicu pravila.
  • P5: 80,00 € − 5,00 € (gornja granica, a ne 8,00 €) + 0,00 € = 75,00 €.

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.

Na času. 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.

Ukratko.

  • Tabela odlučivanja služi za pravila koja zavise od više uslova zajedno.
  • Svaka kolona je kombinacija uslova s ishodom i postaje jedan test.
  • Nemoguće i ekvivalentne kombinacije se spajaju, pa od osam kolona DeliveryApp ostaje pet.
  • Pravljenje tabele otkriva rupe u pravilima prije nego što se napiše kod.

8.5 Prelazi stanja

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).

Slika 8.3 – Dijagram stanja narudžbe: sedam stanja i sedam dozvoljenih prelaza
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 primi

Iz dijagrama se prave dvije vrste testova:

  1. Svaki dozvoljeni prelaz mora raditi. Ima ih 7. Ovo je najmanji zahtjev i zove se pokrivenost prelaza (0-switch coverage).
  2. Svaki nedozvoljeni prelaz mora biti odbijen, a stanje mora ostati isto. Za 7 stanja ima 7 × 7 = 49 parova (stanje, novo stanje), od kojih je 7 dozvoljeno, pa je 42 nedozvoljeno: npr. ISPORUČENA → NOVA, ODBIJENA → PRIHVAĆENA, U_PRIPREMI → OTKAZANA.

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 == staro

Pomoć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.04s

Jedan parametrizovan test, 49 slučajeva, 0,04 sekunde.

Za radoznale. 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.

Ukratko.

  • Testiranje prelaza stanja pravi testove iz dijagrama stanja.
  • Svaki od 7 dozvoljenih prelaza mora raditi, a svaki od 42 nedozvoljena mora biti odbijen bez promjene stanja.
  • Zabrane se češće zaborave od dozvola, pa su testovi nedozvoljenih prelaza posebno važni.
  • Očekivani prelazi u testu prepisuju se iz specifikacije, a ne uvoze iz koda.

Provjeri se.

  1. Koliko pravila ima sređena tabela odlučivanja za dostavu i popust? – Pet (od osam kombinacija, jer su neke nemoguće ili iste).
  2. Koliko košta N-1002 sa STUDENT10 i zašto je dostava besplatna? – 28,80 €; besplatna dostava gleda iznos prije popusta (32,00 €).
  3. Koliko je nedozvoljenih parova stanja narudžbe? – 42, od ukupno 49.

Dio 3 – Kombinacije i iskustvo

8.6 Testiranje po parovima

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.

Česta zabluda. „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.

Ukratko.

  • Broj svih kombinacija raste kao proizvod broja vrijednosti, pa brzo postaje ogroman.
  • Većinu grešaka izaziva jedan parametar ili par parametara, pa se traži da svaki par vrijednosti bude pokriven.
  • Za pet parametara DeliveryApp 9 kombinacija pokriva sve parove umjesto 72.
  • Trojke i veće kombinacije nisu garantovano pokrivene.

8.7 Pogađanje grešaka

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.

Iz stvarnog svijeta. 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.

Ukratko.

  • Pogađanje grešaka koristi iskustvo o mjestima gdje programi često griješe.
  • Tipične kategorije su prazno, nula, negativno, ogromno, decimale, pogrešan tip i čudni znakovi.
  • Ovi testovi često otkriju pitanja na koja pravila ne odgovaraju, a ne samo greške u kodu.
  • Dobar tester vodi svoju listu i dopunjuje je svakom novom greškom.

8.8 Koju tehniku kada

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.

Ukratko.

  • Tehnika se bira prema obliku pravila: rasponi, kombinacije uslova, stanja ili mnogo parametara.
  • Klase i granice daju najviše grešaka po testu i rade se prve.
  • Pogađanje grešaka dopunjuje sistematske tehnike.
  • Na kraju mjerenje pokrivenosti pokaže koji dio koda testovi crne kutije nisu dotakli.

Provjeri se.

  1. Koliko kombinacija ima forma s parametrima od 3, 3, 2, 2 i 2 vrijednosti? – 72; po parovima je dovoljno 9.
  2. Da li testiranje po parovima garantuje da je svaka trojka vrijednosti pokrivena? – Ne, samo svaki par.
  3. Koju tehniku koristite za pravilo PP-6? – Prelaze stanja.

Praktična nastava – Lov na greške D-01 … D-05

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.

Korak 1 – Klase i granice rukom (30 minuta)

  1. Bez gledanja u 8.2 i 8.3, napravite tabelu klasa ekvivalencije i graničnih vrijednosti za pravilo napojnice PP-8 (0, 5, 10 ili 15 %, zaokruženo naviše na 10 centi, najviše 10,00 €).
  2. Za svaku granicu napišite ulaz i očekivani rezultat iz pravila. Uporedite s testovima iz tests/test_napojnica.py (poglavlje 5). Koja granica nedostaje?

Korak 2 – Pokretanje nad v0 (30 minuta)

  1. U 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.
  2. Popunite tabelu: test koji pada, ulaz, očekivano, dobijeno.

Korak 3 – Detektivski rad (60 minuta)

  1. Za svaki pad otvorite deliveryapp_v0.py i nađite red koji ga izaziva. Sada smijete gledati kod.
  2. Grupišite padove po greškama: više padova može imati isti uzrok. Dajte svakoj grešci oznaku D-01, D-02… i kratak opis.
  3. Za svaku grešku zapišite tehniku koja ju je našla (granična vrijednost, klasa ekvivalencije, tabela odlučivanja, prelazi stanja).
  4. Vratite se na nalaz alata ruff iz poglavlja 7 (math uvezen, a nekorišten). Koja greška objašnjava taj nalaz?
  5. Uporedite svoj spisak sa rješenjem u nastavku tek kad završite.

Korak 4 – Testovi koji fale (45 minuta)

  1. Napravite 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?
  2. Napravite 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.
  3. Dodajte bar pet testova iz liste za pogađanje grešaka (8.7). Za one na koje pravila ne odgovaraju zapišite pitanje u rezultati-8.md umjesto testa.

Korak 5 – Web stranica (15 minuta)

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.

Vježbe

Vježba za studenta 1 – klase.

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.

Rješenje. 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.

Vježba za studenta 2 – granice.

Za napojnicu PP-8 navedite granične vrijednosti za gornju granicu od 10,00 € pri 15 % i očekivane rezultate.

Rješenje. 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.

Vježba za studenta 3 – tabela odlučivanja.

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 €.

Rješenje. 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.

Vježba za studenta 4 – prelazi stanja.

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?

Rješenje. 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.

Vježba za studenta 5 – pogađanje.

Napišite pet ulaza za polje „udaljenost” na web formi na kojima biste očekivali grešku i šta pravilo kaže za svaki.

Rješenje. 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.

Sažetak u pet rečenica

  1. Brojač pregleda „Gangnam Style” približio se 2014. najvećem 32-bitnom broju 2 147 483 647, što pokazuje da greške žive na granicama i da ih treba tražiti iz specifikacije.
  2. Klase ekvivalencije dijele ulaze na skupove koji se ponašaju isto, pa je iz svake dovoljan jedan predstavnik, a nevažeće klase se testiraju jedna po jedna.
  3. Analiza graničnih vrijednosti testira granicu i vrijednost odmah do nje, za DeliveryApp 0, 3 i 10 km, te 5,00 €, 30,00 € i 50,00 €, i ona je našla D-01, D-02 i D-05.
  4. Tabela odlučivanja pokriva pravila s više uslova (pet kolona za dostavu i popust), prelazi stanja provjeravaju 7 dozvoljenih i 42 nedozvoljena prelaza narudžbe, a testiranje po parovima smanjuje 72 kombinacije na 9.
  5. Pogađanje grešaka iz iskustva dopunjuje sistematske tehnike, a svih pet grešaka prve verzije DeliveryApp našle su tehnike crne kutije iz pravila, bez gledanja koda.

Pitanja za ponavljanje

  1. Šta se desilo s brojačem pregleda „Gangnam Style” i kakve to veze ima s testiranjem?
  2. Šta je testiranje crnom kutijom i koja je njegova prednost u odnosu na bijelu kutiju?
  3. Šta je klasa ekvivalencije? Navedite klase za udaljenost u DeliveryApp.
  4. Zašto se važeće klase smiju kombinovati u jednom testu, a nevažeće ne?
  5. Zašto su greške na granicama česte?
  6. Koja je razlika između analize graničnih vrijednosti s dvije i s tri vrijednosti?
  7. Navedite granične vrijednosti za pravila PP-1 i PP-4 s očekivanim rezultatima.
  8. Kada se koristi tabela odlučivanja? Kako se iz nje prave testovi?
  9. Kolika je cijena N-1002 sa STUDENT10 i koju rečenicu pravila PP-5 provjerava taj test?
  10. Kako se testiraju prelazi stanja? Koliko dozvoljenih i nedozvoljenih prelaza ima narudžba?
  11. Zašto skup dozvoljenih prelaza u testu ne treba uvoziti iz koda?
  12. Šta je testiranje po parovima i zašto ima smisla?
  13. Šta je pogađanje grešaka? Navedite pet kategorija ulaza.
  14. Nabrojte greške D-01 … D-05 i tehniku koja je našla svaku.

Odgovori na pitanja za ponavljanje.

  1. Brojač je bio 32-bitni cijeli broj s predznakom, s najvećom vrijednošću 2 147 483 647; početkom decembra 2014. YouTube je objavio da ga je spot natjerao na 64-bitni brojač. Pouka je da su greške na granicama, a granica se nalazi iz specifikacije (koliki je najveći broj), ne slučajnim testovima.
  2. Crna kutija pravi testove iz zahtjeva i pravila, ne gledajući kod. Testovi se mogu napisati prije koda, i mogu naći pravilo koje uopšte nije programirano, što bijela kutija ne može.
  3. Klasa ekvivalencije je skup ulaza za koje sistem radi po istom pravilu, pa je dovoljan jedan predstavnik. Za udaljenost: 0 – 3 km (2,00 €), preko 3 do 10 km (+0,50 € po započetom km), manje od 0 i više od 10 km (nevažeće).
  4. Važeće klase se kombinuju jer test treba da prođe, pa jedan test pokrije više njih. Nevažeće se testiraju pojedinačno jer, ako test s dva nevažeća ulaza bude odbijen, ne znamo koja provjera je radila, a koja možda ne.
  5. Jer se lako zamijene < i <=, pravila često ne kažu jasno šta je s granicom, brojanje od nule ili jedinice zbunjuje, a tipovi podataka imaju svoje najveće vrijednosti.
  6. Dvije vrijednosti: granica i prva vrijednost u susjednoj klasi (3 i 3,01 km). Tri vrijednosti: i vrijednost prije granice (2,99, 3 i 3,01 km); hvata više grešaka, ali traži više testova.
  7. PP-1: 4,99 € odbijeno, 5,00 € prihvaćeno (dostava 3,00 € na 5 km). PP-4: 29,99 € plaća 3,00 € dostave na 5 km, 30,00 € ima besplatnu dostavu.
  8. Kad ishod zavisi od više uslova zajedno. Svaki uslov je red, svaka moguća kombinacija kolona s ishodom; nemoguće i iste kolone se spoje, a svaka preostala kolona postaje jedan test s očekivanim rezultatom iz pravila.
  9. 28,80 € (32,00 € − 3,20 € + 0,00 €). Provjerava rečenicu „besplatna dostava gleda iznos prije popusta”, jer je iznos poslije popusta ispod 30,00 €.
  10. Iz dijagrama stanja: svaki dozvoljeni prelaz mora uspjeti, a svaki nedozvoljeni mora biti odbijen bez promjene stanja. Narudžba ima 7 dozvoljenih i 42 nedozvoljena prelaza (od 49 parova).
  11. Jer bi test tada provjeravao kod samim sobom: ako je kod pogrešan (D-04), i očekivanje bi bilo pogrešno, pa test ne bi pao. Očekivanje se prepisuje iz specifikacije.
  12. Bira se mali broj kombinacija tako da se svaki par vrijednosti bilo koja dva parametra pojavi bar jednom. Ima smisla jer većinu grešaka izaziva jedan parametar ili par, a sve kombinacije bi bilo previše (72 prema 9 u DeliveryApp).
  13. Pogađanje grešaka su testovi iz iskustva na mjestima gdje programi često griješe. Kategorije: prazno, nula, negativno, ogromno, decimale, pogrešan tip, razmaci i velika slova, čudni znakovi.
  14. D-01 (> umjesto >= za 30,00 €) – granična vrijednost; D-02 (round umjesto zaokruživanja naviše za kilometre) – granična vrijednost i klasa V2; D-03 (kod malim slovima ne radi) – klasa ekvivalencije s više predstavnika; D-04 (otkazivanje iz U_PRIPREMI) – prelazi stanja; D-05 (nema gornje granice popusta) – granična vrijednost i tabela odlučivanja.