Poglavlje 10: Sistemsko i prihvatno testiranje
Priča za početak. Prvog oktobra 2013. američka vlada otvorila je web stranicu Healthcare.gov, preko koje su milioni građana trebali izabrati i kupiti zdravstveno osiguranje. Bio je to jedan od najvažnijih i najskupljih projekata te vlade, i cijela zemlja je čekala. Stranica je pala skoro odmah. Ljudi su satima gledali poruke o grešci, prazne stranice i beskrajno učitavanje. Prema internim bilješkama objavljenim kasnije, prvog dana je kupovinu osiguranja uspjelo završiti svega šest ljudi, a drugog dana 248. Šta se desilo? Stranica je bila planirana za oko 60 000 istovremenih korisnika, a prvih dana ih je došlo oko 250 000. Ali problem je bio dublji od pogrešne procjene. Nekoliko dana prije otvaranja urađen je test opterećenja, i stranica je pala već s nekoliko stotina istovremenih korisnika. Izvođači su kasnije pred Kongresom rekli da je za testiranje cijelog sistema trebalo nekoliko mjeseci, a dobili su oko dvije sedmice. Dijelove su pravile različite firme, i svaki dio je testiran zasebno, ali cijeli sistem, sa svim dijelovima zajedno i pod pravim opterećenjem, nije bio provjeren na vrijeme. I upozorenje koje je test dao nije zaustavilo otvaranje. Popravka je trajala mjesecima i uključila hitno okupljen tim inženjera iz velikih tehnoloških firmi. Healthcare.gov je postao školski primjer onoga što se dešava kad se sistemsko testiranje i testiranje performansi ostave za kraj, i kad se rezultati testa ne poslušaju.

U poglavlju 9 spojili smo dijelove DeliveryApp: pravila, servise mapa i plaćanja, API. Ovo poglavlje se penje na vrh V-modela iz poglavlja 2. Prvo sistemsko testiranje: cijeli DeliveryApp, na pravom serveru, preko prave mreže, kao što ga koristi kupac. Zatim nefunkcionalno testiranje: ne pitamo „da li radi”, nego „koliko brzo, za koliko korisnika, koliko sigurno i koliko lako”. Na kraju prihvatno testiranje: da li sistem radi ono što kupac i vlasnik proizvoda zaista žele, napisano jezikom koji razumiju svi, u stilu BDD.
Literatura. Osnovna literatura predmeta:
- J. Popović: Priručnik za test inženjere, CET i Računarski fakultet, Beograd, 2023.
- Rakić-Skoković: Priručnik za testiranje softvera, Fakultet tehničkih nauka, Novi Sad, 2013.
- G. J. Myers: The Art of Software Testing, 2. izdanje, John Wiley & Sons, 2004.
- A. P. Mathur: Foundations of Software Testing, Addison-Wesley Professional, 2008.
Za ovo poglavlje: G. Adzic: Specification by Example, Manning, 2011; OWASP Top 10 (owasp.org); dokumentacija alata Locust (docs.locust.io) i pytest-bdd (pytest-bdd.readthedocs.io).
Riječi ovog poglavlja.
- Sistemsko testiranje – testiranje cijelog, sklopljenog sistema u okruženju što sličnijem stvarnom.
- Funkcionalno i nefunkcionalno – funkcionalno pita šta sistem radi (cijena, stanje); nefunkcionalno pita kako to radi (brzina, sigurnost, upotrebljivost).
- Okruženje – računari, baze i podešavanja na kojima sistem radi: razvojno, testno, predprodukcijsko (staging), produkcijsko.
- Vrijeme odziva – koliko korisnik čeka od zahtjeva do odgovora.
- Percentil – vrijednost ispod koje je dati procenat mjerenja; „95. percentil je 16 ms” znači da je 95 % zahtjeva završilo za najviše 16 ms.
- Propusnost (throughput) – broj zahtjeva koje sistem obradi u sekundi.
- Test opterećenja, stresa, skoka i izdržljivosti – testovi performansi s očekivanim, rastućim, naglim i dugotrajnim brojem korisnika.
- OWASP Top 10 – spisak deset najčešćih kategorija sigurnosnih propusta u web aplikacijama.
- Prihvatno testiranje (acceptance testing, UAT) – testiranje koje odlučuje da li korisnik ili naručilac prihvata sistem.
- Kriterij prihvatanja – konkretan, provjerljiv uslov koji korisnička priča mora ispuniti da bi bila „gotova”.
- BDD (Behavior-Driven Development) – razvoj vođen ponašanjem: zahtjevi se pišu kao primjeri ponašanja, jezikom koji razumiju i korisnik i programer, i ti primjeri postaju automatski testovi.
- Gherkin – jezik za BDD scenarije s ključnim riječima Dato, Kada, Zatim (na engleskom Given, When, Then).
Ciljevi poglavlja. Nakon poglavlja možete: objasniti šta je sistemsko testiranje i po čemu se razlikuje od integracionog; nabrojati okruženja i reći zašto test mora ličiti na produkciju; napisati sistemski test koji pokreće pravi server i prolazi cijeli put narudžbe; razlikovati vrste testova performansi; napisati scenarij za Locust i pročitati percentile; prepoznati osnovne sigurnosne propuste iz OWASP Top 10 na primjeru DeliveryApp; objasniti vrste prihvatnog testiranja; napisati kriterije prihvatanja i BDD scenarij na bosanskom jeziku i pokrenuti ga alatom pytest-bdd.
Plan poglavlja – Sedmica 10: tri časa teorije i tri časa praktične nastave
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 10.1–10.3 Sistemsko testiranje; okruženja; put narudžbe od kraja do kraja | Šta sve mora raditi da bi N-1001 stigla kupcu? |
| Teorija 2 | 10.4–10.6 Performanse i Locust; sigurnost; upotrebljivost | 200 korisnika na DeliveryApp |
| Teorija 3 | 10.7–10.9 Prihvatno testiranje; kriteriji prihvatanja; BDD i Gherkin | Scenarij „Dato – Kada – Zatim” |
| Praksa 1–3 | Sistemski test, Locust i BDD scenariji DeliveryApp | tests_sistem/, locustfile.py, tests_bdd/ |
Dio 1 – Sistemsko testiranje
10.1 Cijeli sistem, kao kupac
Sistemsko testiranje provjerava sklopljen sistem u cjelini, u okruženju što sličnijem onom u kojem će ga koristiti ljudi. Na V-modelu (poglavlje 2) ono stoji naspram specifikacije sistema: jedinični testovi provjeravaju dizajn funkcija, integracioni saradnju dijelova, a sistemski da li cijeli sistem ispunjava zahtjeve.
Razlika u odnosu na integraciju iz poglavlja 9:
| Integraciono | Sistemsko | |
|---|---|---|
| Šta se testira | saradnja dva ili više dijelova | cijeli sistem |
| Ko obično testira | programeri | nezavisni testni tim |
| Spoljni servisi | često dvojnici | pravi ili vjerne kopije |
| Okruženje | računar programera | testno okruženje slično produkciji |
| Zahtjevi | funkcionalni (šta) | funkcionalni i nefunkcionalni (šta i kako) |
Slika iz života: tehnički pregled auta. Mehaničar je ranije provjerio kočnice i motor odvojeno (jedinični testovi) i kako rade zajedno (integracija). Na tehničkom pregledu se auto provjerava cijeli, onakav kakav izlazi na cestu: svjetla, kočnice, izduvni gasovi, sve odjednom, po propisanoj listi.
Sistemsko testiranje je skoro uvijek crna kutija: tester gleda sistem spolja, kao korisnik, i koristi tehnike iz poglavlja 8 (klase, granice, tabele, stanja) nad cijelim sistemom.
Ukratko.
- Sistemsko testiranje provjerava sklopljen sistem u cjelini, u okruženju sličnom stvarnom.
- Na V-modelu stoji naspram specifikacije sistema i provjerava funkcionalne i nefunkcionalne zahtjeve.
- Obično ga radi nezavisan testni tim, tehnikama crne kutije.
- Za razliku od integracije, koristi prave ili vjerne kopije spoljnih servisa.
10.2 Okruženja
Program koji radi „na mom računaru” ne mora raditi na serveru. Zato sistemi obično imaju nekoliko okruženja:
| Okruženje | Za šta | Podaci |
|---|---|---|
| razvojno (development) | programer piše i probava kod | izmišljeni, mali |
| testno (test, QA) | testeri testiraju novu verziju | testni skup (N-1001, K-01…) |
| predprodukcijsko (staging) | posljednja proba, što sličnije produkciji | kopija strukture produkcije, bez stvarnih ličnih podataka |
| produkcijsko (production) | stvarni korisnici | stvarni |
Sistemski test ima smisla samo ako okruženje liči na produkciju: ista verzija Pythona, ista baza, ista podešavanja, slična količina podataka. Healthcare.gov je dobar primjer suprotnog: dijelovi su testirani u svojim okruženjima, a cijeli sistem skoro nikako.
Dvije zamke:
- Razlike u okruženju. Test prolazi na testnom serveru s 10 narudžbi u bazi, a produkcija s 10 miliona je spora. Ili test koristi drugi format datuma od produkcije.
- Stvarni podaci u testu. Kopirati produkcijsku bazu s imenima, adresama i karticama kupaca na testni server je zabranjeno zakonima o zaštiti podataka (GDPR u EU, zakon o zaštiti ličnih podataka u BiH) i opasno. Testni podaci se izmišljaju ili se stvarni podaci anonimizuju. Ista zabrana važi za AI razgovore: stvarni podaci kupaca ne idu u AI alat.
Ukratko.
- Sistem prolazi kroz razvojno, testno, predprodukcijsko i produkcijsko okruženje.
- Sistemski test ima smisla samo u okruženju koje liči na produkciju.
- Razlike u okruženju (verzije, količina podataka, podešavanja) česte su uzrok grešaka koje se vide tek u produkciji.
- Stvarni lični podaci ne smiju u testno okruženje ni u AI alat; koriste se izmišljeni ili anonimizovani.
10.3 Put narudžbe od kraja do kraja
Šta sve mora raditi da bi N-1001 stigla kupcu? Server mora biti pokrenut, mreža mora raditi, API mora primiti JSON, pravila moraju izračunati 24,50 €, narudžba mora proći pet stanja, i kupac mora vidjeti istoriju. Sistemski test sve to provjerava jednim tokom, kao pravi korisnik.
Fajl primjeri/deliveryapp/tests_sistem/test_sistem.py za to pokrene pravi server (uvicorn api:app) kao poseban proces i šalje mu HTTP zahtjeve preko mreže, običnom bibliotekom urllib, kao što bi to radio bilo koji klijent:
@pytest.fixture(scope="module")
def server():
"""Pokrene server, sačeka da odgovori, a na kraju ga ugasi."""
proces = subprocess.Popen([sys.executable, "-m", "uvicorn", "api:app", "--port", "8765"], cwd=FOLDER)
... # čeka dok server ne odgovori
yield ADRESA
proces.terminate()
def test_put_narudzbe_od_pocetka_do_kraja(server):
# 1. kupac pogleda jelovnik Picerije
status, jela = zahtjev("GET", "/v1/jela?restoran=R-1")
assert status == 200 and len(jela) == 3
# 2. naruči N-1001: 21,50 € + 3,00 € dostave
status, n = zahtjev("POST", "/v1/narudzbe", N1001)
assert status == 201
assert (n["stanje"], n["ukupno"]) == ("NOVA", 2450)
broj = n["broj"]
# 3. restoran, kuhinja i dostavljač vode narudžbu do kupca
for stanje in ["PRIHVAĆENA", "U_PRIPREMI", "NA_PUTU", "ISPORUČENA"]:
status, _ = zahtjev("PATCH", f"/v1/narudzbe/{broj}/stanje", {"stanje": stanje})
assert status == 200, stanje
# 4. kupac vidi cijelu istoriju
status, n = zahtjev("GET", f"/v1/narudzbe/{broj}")
assert n["istorija"] == ["NOVA", "PRIHVAĆENA", "U_PRIPREMI", "NA_PUTU", "ISPORUČENA"]Fixture ima scope="module": server se pokrene jednom za sve testove u fajlu, a ne za svaki test posebno, jer pokretanje traje.
pytest -q tests_sistem
... [100%]
3 passed in 0.92s
Nad prvom verzijom (DeliveryApp=v0) padaju put narudžbe (ukupno 24,00 € umjesto 24,50 €, D-02) i otkazivanje u pripremi (200 umjesto 409, D-04):
FAILED tests_sistem/test_sistem.py::test_put_narudzbe_od_pocetka_do_kraja - A...
FAILED tests_sistem/test_sistem.py::test_otkazivanje_u_pripremi_odbijeno - as...
2 failed, 1 passed in 0.70s
Isti nalazi kao kroz TestClient u poglavlju 9? Da, i to je dobro: sistemski test ne treba da nalazi nove greške u pravilima, njih su našli niži nivoi. Njegov posao je da nađe ono što niži nivoi ne vide: server se ne pokreće, port je zauzet, JSON se pogrešno kodira preko mreže, slova Ć i Ž se pokvare, nedostaje biblioteka na serveru. Zato je sistemskih testova malo (vrh piramide iz poglavlja 4), ali moraju postojati.
Na času. Šta bi sistemski test otkrio, a TestClient test ne bi? Navedite tri primjera. – Na primjer: server ne može da se pokrene jer nedostaje
uvicorn; port 8000 je zauzet drugim programom; pogrešno podešen zapis znakova pa „PRIHVAĆENA” stiže kao „PRIHVAĆENA”; zaštitni zid (firewall) blokira zahtjeve.
Ukratko.
- Sistemski test prolazi cijeli put korisnika jednim tokom: jelovnik, narudžba, sva stanja, istorija.
- Test u DeliveryApp pokreće pravi server kao poseban proces i šalje zahtjeve preko mreže.
- Fixture s
scope="module"pokreće server jednom za sve testove u fajlu.- Sistemskih testova je malo, a hvataju ono što niži nivoi ne vide: pokretanje, mrežu, kodiranje, okruženje.
Provjeri se.
- Po čemu se sistemsko testiranje razlikuje od integracionog? – Testira cijeli sistem u okruženju sličnom produkciji, s pravim servisima, i provjerava i nefunkcionalne zahtjeve.
- Smiju li se u testnom okruženju koristiti stvarni podaci kupaca? – Ne, samo izmišljeni ili anonimizovani.
- Zašto fixture servera ima
scope="module"? – Da se server pokrene jednom za sve testove u fajlu, jer pokretanje traje.
Dio 2 – Nefunkcionalno testiranje
10.4 Performanse i Locust
Funkcionalni test pita: „Da li je cijena N-1001 24,50 €?” Test performansi pita: „Da li je cijena N-1001 24,50 € i kad u isto vrijeme pita još 199 ljudi, i koliko dugo svako čeka?”
Za DeliveryApp imamo nefunkcionalni zahtjev:
NZ-1. Pri 200 istovremenih korisnika, 95 % zahtjeva mora završiti za najviše 2 sekunde, bez grešaka.
Primijetite oblik zahtjeva: ima broj korisnika, procenat i granicu vremena. Zahtjev „stranica mora biti brza” ne može se testirati; NZ-1 može.
Postoje četiri glavne vrste testova performansi (slika 10.2):

| Vrsta | Pitanje | DeliveryApp |
|---|---|---|
| opterećenje (load) | radi li sistem pri očekivanom broju korisnika? | 200 korisnika, NZ-1 |
| stres (stress) | gdje je granica i šta se desi kad je pređemo? | povećavati dok ne padne |
| skok (spike) | podnosi li nagli skok? | petak u 19 h, reklama na TV-u |
| izdržljivost (soak, endurance) | radi li satima i danima bez kvarenja? | 150 korisnika 8 sati; traži „curenje” memorije |
Za mjerenje treba alat koji glumi stotine korisnika. Locust je takav alat za Python: korisnik se opisuje kao klasa, a Locust pokrene koliko god kopija treba. Fajl primjeri/deliveryapp/locustfile.py:
class Kupac(HttpUser):
"""Jedan virtuelni kupac: gleda jelovnik, provjerava cijenu i ponekad naruči."""
wait_time = between(1, 3) # kupac „razmišlja” 1 do 3 sekunde između klikova
@task(5)
def jelovnik(self):
self.client.get("/v1/jela", params={"restoran": random.choice(["R-1", "R-2", "R-3"])})
@task(3)
def cijena(self):
with self.client.post("/v1/cijena", json=N1001, catch_response=True) as r:
if r.status_code != 200 or r.json().get("ukupno") != 2450:
r.failure(f"pogrešna cijena: {r.status_code} {r.text[:80]}")
@task(1)
def narudzba(self):
self.client.post("/v1/narudzbe", json=N1001)Brojevi u @task(5), @task(3), @task(1) su težine: od 9 radnji, prosječan kupac 5 puta pogleda jelovnik, 3 puta provjeri cijenu i jednom naruči, kao pravi kupci. Primijetite i da test performansi provjerava rezultat: cijena mora biti 24,50 €. Brz odgovor s pogrešnim iznosom nije uspjeh.
Pokretanje: u jednom terminalu server, u drugom Locust bez grafičkog prikaza (200 korisnika, 20 novih u sekundi, 30 sekundi):
uvicorn api:app --port 8000
locust -f locustfile.py --headless -u 200 -r 20 -t 30s --host http://127.0.0.1:8000
Skraćen rezultat jednog mjerenja na laptopu:
Type Name # reqs # fails | Avg Min Max Med | req/s
POST /v1/cijena 864 0(0.00%) | 4 0 37 3 | 29.44
POST /v1/narudzbe 285 0(0.00%) | 4 0 36 3 | 9.71
Aggregated 2584 0(0.00%) | 4 0 43 3 | 88.05
Response time percentiles (approximated)
Type Name 50% 66% 75% 80% 90% 95% 98% 99% 100%
Aggregated 3 4 5 6 8 16 18 22 44

Kako čitati:
- 2584 zahtjeva, 0 grešaka, oko 88 zahtjeva u sekundi (propusnost);
- medijan 3 ms: pola zahtjeva završi za 3 ms ili brže;
- 95. percentil 16 ms: to je broj iz NZ-1, i daleko je ispod 2000 ms; zahtjev je ispunjen;
- najsporiji zahtjev 44 ms.
Zašto percentil, a ne prosjek? Prosjek sakriva loše slučajeve. Ako 95 kupaca čeka 0,1 s, a 5 čeka 30 s, prosjek je oko 1,6 s i „izgleda dobro”, a pet ljudi je zatvorilo aplikaciju. 95. percentil bi pokazao 30 s.
Česta zabluda. „Naš API odgovara za 16 ms, dakle DeliveryApp podnosi 200 korisnika.” Ovaj API čuva podatke u memoriji, nema bazu, nema pravi servis mapa ni banku, i radi na istom laptopu kao Locust. U stvarnom sistemu svaki od tih dijelova dodaje vrijeme, i baš oni obično prvi „pucaju”. Rezultat testa performansi važi samo za okruženje u kojem je izmjeren. Zato se ozbiljni testovi performansi rade u predprodukciji, na kopiji pravog sistema.
Ukratko.
- Test performansi provjerava brzinu i ispravnost pri mnogo istovremenih korisnika, prema mjerljivom zahtjevu kao NZ-1.
- Vrste su opterećenje, stres, skok i izdržljivost.
- Locust opisuje korisnika kao klasu s težinskim radnjama i pokreće stotine kopija.
- Percentil je bolji od prosjeka jer pokazuje koliko čekaju najsporiji korisnici; za DeliveryApp je 95. percentil 16 ms.
- Rezultat važi samo za okruženje u kojem je izmjeren.
10.5 Sigurnost
Sigurnosno testiranje pita: šta može uraditi neko ko ne bi smio? Za web aplikacije postoji poznat spisak, OWASP Top 10, deset najčešćih kategorija propusta, koji se povremeno ažurira. Nekoliko kategorija na primjeru DeliveryApp:
| Kategorija | Pitanje za DeliveryApp | Stanje u našem API-ju |
|---|---|---|
| pokvarena kontrola pristupa | može li kupac K-02 promijeniti narudžbu kupca K-01? Može li kupac sam proglasiti narudžbu isporučenom? | da, može: API nema prijavu ni provjeru ko šalje zahtjev |
| ubacivanje (injection) | šta ako polje kupac sadrži SQL ili HTML kod? |
nema baze ni HTML-a, ali polje se ne provjerava |
| nesiguran dizajn | vjeruje li server cijeni koju šalje klijent? | ne: server sam računa cijenu (dobro) |
| pogrešna podešavanja | da li greška otkriva detalje servera? | FastAPI ne vraća trag greške korisniku (dobro) |
| ranjive komponente | koriste li se biblioteke s poznatim ranjivostima? | treba redovno provjeravati (pip-audit) |
Prvi red je pravi nalaz: u našem API-ju svako može poslati PATCH /v1/narudzbe/N-1001/stanje sa "ISPORUČENA". Za učionicu je to u redu, ali za pravi sistem je to ozbiljan propust: treba prijava (ko si?) i ovlaštenje (smiješ li to?). Kupac smije samo otkazati, restoran prihvatiti i odbiti, dostavljač preuzeti i isporučiti.
Takav nalaz se zapisuje kao izvještaj o defektu (poglavlje 3), s ozbiljnošću „visoka” i koracima za ponavljanje. Sigurnosni test za njega bi izgledao ovako (kad prijava postoji): kupac K-02 pokuša promijeniti narudžbu kupca K-01, očekivano 403 Forbidden.
Iz stvarnog svijeta. Heartbleed (poglavlje 7) je bio „ranjiva komponenta” za svaku firmu koja je koristila OpenSSL: tuđa greška u biblioteci postala je njihova. Zato timovi danas automatski provjeravaju biblioteke koje koriste, alatima kao
pip-auditza Python ili Dependabot na GitHubu.
Ukratko.
- Sigurnosno testiranje pita šta može uraditi neko ko ne bi smio.
- OWASP Top 10 je spisak najčešćih kategorija propusta u web aplikacijama.
- API DeliveryApp ima pokvarenu kontrolu pristupa: svako može promijeniti stanje bilo koje narudžbe.
- Server dobro radi što sam računa cijenu i ne otkriva detalje greške.
10.6 Upotrebljivost i pristupačnost
Sistem može biti tačan, brz i siguran, a da ga ljudi ne znaju koristiti. Upotrebljivost (usability) pita: može li prosječan kupac naručiti N-1001 bez pomoći, brzo i bez greške? Testira se tako što se stvarni ljudi posmatraju dok rade zadatak („naručite dvije Margarite i sok na adresu Lipa 12 s kodom za studente”), a mjeri se:
- da li su uspjeli,
- koliko je trajalo,
- gdje su zastali ili pogriješili,
- koliko su zadovoljni (kratka anketa).
Već pet korisnika obično pokaže najveće probleme. Primjer nalaza: korisnici ne vide polje za kod popusta jer je ispod dugmeta „Naruči”.
Pristupačnost (accessibility) pita: mogu li stranicu koristiti i ljudi sa slabijim vidom, slijepi (uz čitač ekrana), ljudi koji ne mogu koristiti miš? Pravila su u međunarodnom standardu WCAG. Primjeri: dugmad imaju tekstualni opis (u stranici DeliveryApp aria-label="Dodaj Pizza Margarita"), kontrast teksta je dovoljan, sve se može uraditi tastaturom. Pristupačnost djelimično provjeravaju automatski alati, a detaljnije je radimo u poglavlju 13.
Ukratko.
- Upotrebljivost se testira posmatranjem stvarnih ljudi dok rade zadatak; već pet korisnika pokaže najveće probleme.
- Mjeri se uspjeh, vrijeme, mjesta zastoja i zadovoljstvo.
- Pristupačnost znači da stranicu mogu koristiti i ljudi s invaliditetom, po standardu WCAG.
- Dio pristupačnosti provjeravaju automatski alati, što radimo u poglavlju 13.
Provjeri se.
- Zašto je 95. percentil bolji od prosjeka? – Prosjek sakriva najsporije korisnike, a percentil pokazuje koliko oni čekaju.
- Koja je razlika između testa opterećenja i testa stresa? – Opterećenje provjerava očekivani broj korisnika; stres povećava broj dok sistem ne padne, da se nađe granica.
- Koji sigurnosni propust ima API DeliveryApp? – Pokvarena kontrola pristupa: svako može promijeniti stanje bilo koje narudžbe.
Dio 3 – Prihvatno testiranje i BDD
10.7 Prihvatno testiranje
Sistem je prošao sve testove tima. Da li je gotov? Ne dok ga ne prihvati onaj za koga je napravljen. Prihvatno testiranje je posljednji nivo V-modela, naspram korisničkih zahtjeva, i ne pita „da li radi po specifikaciji”, nego „da li je ovo ono što nam treba”.
Vrste prihvatnog testiranja:
| Vrsta | Ko testira | Primjer za DeliveryApp |
|---|---|---|
| korisničko (UAT) | stvarni korisnici ili njihovi predstavnici | vlasnici tri restorana probaju aplikaciju sedmicu dana |
| operativno | administratori i podrška | rezervne kopije, vraćanje poslije pada, nadzor servera |
| ugovorno | naručilac prema ugovoru | grad koji je naručio aplikaciju provjerava stavke ugovora |
| regulatorno | prema zakonu ili standardu | zaštita ličnih podataka, fiskalni računi |
| alfa | korisnici, kod proizvođača | studenti EUBD-a naručuju u učionici |
| beta | korisnici, u stvarnom svijetu | 100 kupaca koristi aplikaciju mjesec dana prije svih |
Prihvatni testovi se ne pišu na kraju. Pišu se na početku, zajedno sa zahtjevima, kao kriteriji prihvatanja: konkretni, provjerljivi uslovi koje priča mora ispuniti. Korisnička priča za DeliveryApp:
Priča. Kao kupac, želim unaprijed znati koliko plaćam dostavu, da bih mogao odlučiti koliko naručujem.
Kriteriji prihvatanja.
- Za korpu od 30,00 € ili više dostava je 0,00 €.
- Za korpu od 29,99 € na 5 km dostava je 3,00 €.
- Kod STUDENT10 ne ukida besplatnu dostavu: 32,00 € s kodom košta 28,80 €.
- Cijena dostave se vidi prije dugmeta „Naruči”.
Prva tri kriterija su upravo testovi iz poglavlja 8. Četvrti je kriterij upotrebljivosti i provjerava se ručno ili testom web stranice (poglavlje 13). Kad kriteriji postoje prije koda, svi znaju šta znači „gotovo”.
Ukratko.
- Prihvatno testiranje odlučuje da li korisnik ili naručilac prihvata sistem.
- Postoje korisničko, operativno, ugovorno, regulatorno, alfa i beta prihvatno testiranje.
- Kriteriji prihvatanja su konkretni, provjerljivi uslovi koji se pišu zajedno s korisničkom pričom.
- Kad kriteriji postoje prije koda, svi znaju šta znači da je priča gotova.
10.8 BDD: primjeri koje razumiju svi
Kriteriji prihvatanja su napisani običnim jezikom. Testovi su napisani u Pythonu. Između njih je jaz: vlasnik restorana ne čita Python, a programer može pogrešno prevesti rečenicu u kod. BDD (Behavior-Driven Development, razvoj vođen ponašanjem) zatvara taj jaz: kriteriji se pišu kao scenariji u strogo propisanom obliku, koji je i dalje običan jezik, ali ga alat može izvršiti kao test.
Jezik scenarija zove se Gherkin. Ima nekoliko ključnih riječi, a postoji i na bosanskom:
| Engleski | Bosanski | Značenje |
|---|---|---|
| Feature | Karakteristika | šta opisujemo (korisnička priča) |
| Scenario | Scenario | jedan primjer ponašanja |
| Given | Dato | početno stanje (pripremi) |
| When | Kada | radnja korisnika (izvrši) |
| Then | Zatim | očekivani ishod (provjeri) |
| And, But | I, Ali | nastavak prethodnog koraka |
| Scenario Outline, Examples | Scenariju-obris, Primjeri | jedan scenarij za tabelu primjera |
Prepoznajete li obrazac? Dato – Kada – Zatim je isto što i pripremi – izvrši – provjeri iz poglavlja 5, samo napisano za ljude.
Fajl primjeri/deliveryapp/tests_bdd/features/dostava.feature:
# language: bs
Karakteristika: Cijena dostave i otkazivanje
Kao kupac DeliveryApp
želim unaprijed znati koliko plaćam dostavu
da bih mogao odlučiti koliko naručujem
Scenario: Narudžba od tačno 30,00 € ima besplatnu dostavu
Dato korpa od 30,00 €
I adresa udaljena 5 km
Kada kupac zatraži cijenu
Zatim dostava je 0,00 €
I ukupno za plaćanje je 30,00 €
Scenario: Studentski popust ne ukida besplatnu dostavu
Dato korpa od 32,00 €
I adresa udaljena 2,5 km
I kod popusta "student10"
Kada kupac zatraži cijenu
Zatim dostava je 0,00 €
I ukupno za plaćanje je 28,80 €
Scenariju-obris: Dostava zavisi od iznosa i udaljenosti
Dato korpa od <iznos> €
I adresa udaljena <km> km
Kada kupac zatraži cijenu
Zatim dostava je <dostava> €
Primjeri:
| iznos | km | dostava |
| 10,00 | 3 | 2,00 |
| 10,00 | 3,1 | 2,50 |
| 21,50 | 4,2 | 3,00 |
| 29,99 | 5 | 3,00 |
| 30,00 | 5 | 0,00 |
Prvi red, # language: bs, kaže alatu da su ključne riječi na bosanskom. Ovaj fajl može pročitati, ispraviti i potpisati vlasnik restorana. Iznosi su napisani kako ih piše kupac, s decimalnim zarezom i znakom €.
Ukratko.
- BDD piše kriterije prihvatanja kao scenarije koje razumiju svi, a alat ih izvršava kao testove.
- Jezik scenarija je Gherkin, s ključnim riječima Karakteristika, Scenario, Dato, Kada, Zatim, I.
- Dato – Kada – Zatim je isti obrazac kao pripremi – izvrši – provjeri.
- Scenariju-obris s tabelom Primjeri pravi jedan scenarij za svaki red tabele.
10.9 Koraci u Pythonu i pytest-bdd
Da bi scenarij postao test, svaki korak treba povezati s malom Python funkcijom. To radi biblioteka pytest-bdd (pip install pytest-bdd). Fajl tests_bdd/test_dostava_bdd.py:
from pytest_bdd import given, parsers, scenarios, then, when
from deliveryapp import Narudzba, cijena_dostave, popust
scenarios("features/dostava.feature")
def centi(tekst):
"""'30,00' -> 3000 (iznos kako ga piše kupac -> centi)."""
eura, _, c = tekst.strip().partition(",")
return int(eura) * 100 + int(c or 0)
@given(parsers.parse("korpa od {iznos} €"))
def korpa(narudzba_podaci, iznos):
narudzba_podaci["iznos"] = centi(iznos)
@when("kupac zatraži cijenu")
def zatrazi_cijenu(narudzba_podaci):
d = narudzba_podaci
d["dostava"] = cijena_dostave(d["km"], d["iznos"])
d["ukupno"] = d["iznos"] - popust(d["iznos"], d["kod"]) + d["dostava"]
@then(parsers.parse("dostava je {iznos} €"))
def provjeri_dostavu(narudzba_podaci, iznos):
assert narudzba_podaci["dostava"] == centi(iznos)Kako radi:
scenarios(...)pročita fajl i od svakog scenarija napravi pytest test;@given(parsers.parse("korpa od {iznos} €"))kaže: korak koji počinje sa „Dato korpa od …” izvršava ova funkcija, a ono što piše na mjestu{iznos}stiže kao parametar;- funkcija
centipretvara iznos kako ga piše kupac (30,00) u cente (3000), jer pravila rade u centima (poglavlje 5).
pytest -v tests_bdd
tests_bdd/test_dostava_bdd.py::test_studentski_popust_ne_ukida_besplatnu_dostavu PASSED
tests_bdd/test_dostava_bdd.py::test_dostava_zavisi_od_iznosa_i_udaljenosti[10,00-3,1-2,50] PASSED
...
8 passed in 0.06s
Četiri scenarija daju 8 testova (obris s pet primjera daje pet). Nad prvom verzijom pada 6 od 8: tačno 30,00 € (D-01), kod malim slovima (D-03), 3,1 km i 4,2 km (D-02), 30,00 € u tabeli (D-01) i otkazivanje u pripremi (D-04). Dva prolaze: 3 km i 29,99 €, jer tu prva verzija slučajno računa tačno.
Najveća vrijednost BDD-a nije u alatu, nego u razgovoru. Scenariji nastaju na sastanku na kojem sjede vlasnik proizvoda, programer i tester (tzv. „tri amigosa”) i zajedno smišljaju primjere: „A šta ako je korpa tačno 30,00 €? A ako s popustom padne ispod 30,00 €?” Svako pitanje koje se tu postavi je greška koja se neće napisati.
Česta zabluda. „BDD znači pisati sve testove u Gherkinu.” Ne. Gherkin je za ponašanje važno korisniku, desetine scenarija, a ne stotine. Granice od 0,01 km, 49 prelaza stanja i 200 nizova modela ostaju u običnim pytest testovima, gdje su kraći i brži. Ako vlasnik proizvoda ne bi htio pročitati scenarij, on ne pripada u
.featurefajl.
Ukratko.
- Biblioteka pytest-bdd povezuje svaki korak scenarija s Python funkcijom i pravi pytest testove.
- Parametri koraka, kao
{iznos}, prenose vrijednosti iz rečenice u funkciju.- Četiri scenarija DeliveryApp daju osam testova, a nad prvom verzijom šest pada.
- Najveća vrijednost BDD-a je zajednički razgovor o primjerima prije pisanja koda.
- U Gherkin idu scenariji važni korisniku, a detaljni testovi ostaju u pytestu.
Provjeri se.
- Šta je kriterij prihvatanja? – Konkretan, provjerljiv uslov koji korisnička priča mora ispuniti da bi bila gotova.
- Kako se na bosanskom pišu Given, When, Then u Gherkinu? – Dato, Kada, Zatim.
- Koliko testova pravi Scenariju-obris s tabelom od pet redova? – Pet, jedan za svaki red.
Praktična nastava – Sistem, opterećenje i prihvatanje
Cilj je pokrenuti sistemski test s pravim serverom, izmjeriti performanse DeliveryApp Locustom, prijaviti sigurnosni nalaz i napisati BDD scenarije za novu korisničku priču. Radi se u parovima. Prije početka: pip install fastapi uvicorn locust pytest-bdd.
Korak 1 – Sistemski test (30 minuta)
- U
primjeri/deliveryapppokrenitepytest -v tests_sistemnad ispravnom i nad prvom verzijom. Zapišite rezultate. - Dodajte sistemski test: narudžba N-1002 (4 × Ćevapi, 2,5 km) prolazi put NOVA → PRIHVAĆENA → OTKAZANA. Očekivani iznos i stanja zapišite prije pokretanja.
- Pokušajte pokrenuti test dok je port 8765 zauzet (pokrenite prije toga
uvicorn api:app --port 8765u drugom terminalu). Šta se desi? Kakav je to nalaz: greška u kodu ili u okruženju?
Korak 2 – Locust (45 minuta)
- Pokrenite server (
uvicorn api:app --port 8000) i Locust s grafičkim prikazom (locust -f locustfile.py --host http://127.0.0.1:8000), otvoritehttp://localhost:8089, unesite 200 korisnika i 20 u sekundi. - Poslije jedne minute zapišite: broj zahtjeva, greške, propusnost, medijan, 95. percentil. Da li je NZ-1 ispunjen?
- Stres: povećavajte broj korisnika (500, 1000, 2000…) dok vrijeme odziva ne poraste ili se ne pojave greške. Zapišite gdje je granica na vašem računaru i ko je „pukao” prvi: server ili sam Locust (pogledajte opterećenje procesora u Task Manageru).
- Dodajte u
locustfile.pyradnju koja provjerava kod STUDENT10 (ukupnomora biti 2235) i pokrenite ponovo.
Korak 3 – Sigurnost (30 minuta)
- Preko stranice
/docsnapravite narudžbu, pa je, bez ikakve prijave, proglasite isporučenom. Napišite izvještaj o defektu (šablon iz poglavlja 3) s ozbiljnošću, prioritetom i koracima. - Pošaljite narudžbu u kojoj je
kupacvrlo dug tekst (10 000 znakova) i tekst<script>alert(1)</script>. Šta API vraća? Šta bi se moglo desiti kad web stranica prikaže to ime? Zapišite kao pitanje za tim.
Korak 4 – BDD (60 minuta)
- Pokrenite
pytest -v tests_bddnad obje verzije i uporedite padove s listom D-01 … D-05 iz poglavlja 8. - U trojkama (vlasnik proizvoda, programer, tester) napišite fajl
features/napojnica.featureza priču: „Kao kupac, želim ostaviti napojnicu dostavljaču, da bih ga nagradio za brzu dostavu.” Kriteriji iz PP-8. Bar jedan Scenario i jedan Scenariju-obris s pet primjera (među njima granice 66,00 € i 66,01 € za 15 %, vježba 2 poglavlja 8). - Napišite korake u
test_napojnica_bdd.pykoji koriste funkcijunapojnicaiz poglavlja 5 i pokrenite ih. - Dajte
.featurefajl kolegi iz druge grupe koji nije učestvovao. Može li ga razumjeti bez objašnjenja? Šta je bilo nejasno?
Rezultat koji se predaje: fajl rezultati-10.md s rezultatima sistemskog testa i opisom problema sa zauzetim portom; tabela mjerenja Locusta za 200 korisnika i granica iz testa stresa; izvještaj o sigurnosnom defektu; features/napojnica.feature i test_napojnica_bdd.py s rezultatom pokretanja; komentar kolege o razumljivosti scenarija.
Vježbe
Vježba za studenta 1 – vrsta testa.
Koja je vrsta testa: a) 150 korisnika 12 sati, prati se memorija servera; b) za 10 sekundi broj korisnika skoči sa 50 na 2000; c) korisnici se dodaju dok vrijeme odziva ne pređe 5 s; d) 200 korisnika, provjerava se NZ-1; e) vlasnik restorana R-2 sedmicu dana prima narudžbe preko aplikacije.
Rješenje. a) izdržljivost; b) skok; c) stres; d) opterećenje; e) korisničko prihvatno testiranje (UAT), ili beta, ako su uključeni i pravi kupci.
Vježba za studenta 2 – percentili.
Deset zahtjeva trajalo je (u ms): 20, 22, 25, 25, 30, 31, 35, 40, 45, 3000. Koliki je prosjek, a koliki medijan? Šta bolje opisuje iskustvo korisnika?
Rješenje. Zbir je 3273 ms, prosjek 327,3 ms. Medijan (prosjek 5. i 6. vrijednosti) je 30,5 ms. Medijan opisuje tipičnog korisnika (oko 30 ms), a prosjek je „naduvan” jednim zahtjevom od 3 s. Ali ni medijan ne pokazuje tog jednog korisnika koji je čekao 3 s; zato se gledaju i visoki percentili (95., 99.) i maksimum.
Vježba za studenta 3 – nefunkcionalni zahtjev.
Prepravite zahtjev „Aplikacija mora biti brza i podnositi mnogo korisnika” tako da se može testirati.
Rješenje. Npr.: „Pri 500 istovremenih korisnika, od kojih svaki napravi radnju svakih 1 do 3 sekunde, 95 % zahtjeva za cijenu i narudžbu završava za najviše 1 sekundu, 99 % za najviše 3 sekunde, a udio grešaka je manji od 0,1 %.” Zahtjev ima broj korisnika, ponašanje korisnika, percentile, granice vremena i dozvoljene greške.
Vježba za studenta 4 – Gherkin.
Napišite scenarij na bosanskom za pravilo PP-1 (najmanja narudžba 5,00 €), s granicom.
Rješenje.
# language: bs
Karakteristika: Najmanja narudžba
Scenariju-obris: Narudžba ispod 5,00 € se odbija
Dato korpa od <iznos> €
I adresa udaljena 2 km
Kada kupac zatraži cijenu
Zatim narudžba je <ishod>
Primjeri:
| iznos | ishod |
| 4,99 | odbijena |
| 5,00 | prihvaćena |
Za korak „narudžba je …” treba nova funkcija koraka koja hvata ValueError (kao korak za otkazivanje u test_dostava_bdd.py).
Vježba za studenta 5 – okruženje.
Tim je za testno okruženje kopirao produkcijsku bazu DeliveryApp sa svim kupcima, adresama i brojevima telefona, „da testovi budu realni”. Šta nije u redu i kako to uraditi ispravno?
Rješenje. Stvarni lični podaci kupaca ne smiju biti u testnom okruženju bez zakonskog osnova i zaštite (GDPR, zakon o zaštiti ličnih podataka u BiH): više ljudi ima pristup, zaštita je slabija, a podaci mogu procuriti. Ispravno: izmišljeni podaci (K-01 … K-05, adrese kao „Lipa 12”) ili anonimizovana kopija u kojoj su imena, adrese i telefoni zamijenjeni, a zadržani su samo struktura i količina podataka potrebni za test performansi.
Sažetak u pet rečenica
- Healthcare.gov je 2013. pao prvog dana jer cijeli sistem nije na vrijeme testiran pod pravim opterećenjem, a test koji je pokazao pad s nekoliko stotina korisnika nije zaustavio otvaranje.
- Sistemsko testiranje provjerava sklopljen sistem u okruženju sličnom produkciji, a sistemski test DeliveryApp pokreće pravi server i prolazi cijeli put narudžbe preko mreže.
- Nefunkcionalno testiranje mjeri performanse (opterećenje, stres, skok, izdržljivost), sigurnost i upotrebljivost, a Locust je pokazao da 95 % zahtjeva DeliveryApp završi za 16 ms pri 200 korisnika, uz napomenu da rezultat važi samo za to okruženje.
- Sigurnosni pregled po OWASP Top 10 otkrio je pravi propust u API-ju DeliveryApp: svako može promijeniti stanje bilo koje narudžbe.
- Prihvatno testiranje odlučuje da li korisnik prihvata sistem, a BDD piše kriterije prihvatanja kao Gherkin scenarije na bosanskom (Dato, Kada, Zatim), koje razumiju svi i koje pytest-bdd izvršava kao testove.