Dio 1 – Zašto i kada automatizovati
Dio 2 – Piramida testova i alati
Dio 3 – Prvi automatski test
Praksa – prvi automatski testovi
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 4 (Sedmica 4, 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 |
Netflix je oko 2010. godine prebacio svoj sistem u oblak, na hiljade iznajmljenih servera. Inženjeri su znali da će u takvom sistemu neki server svakog dana negdje otkazati: tako rade veliki računski centri. Pitanje nije bilo „hoće li otkazati”, nego „hoće li ostatak sistema to izdržati”. Umjesto da čekaju da kvar dođe sam, u nedjelju u tri ujutro, napravili su program sa šaljivim imenom Chaos Monkey („majmun haosa”). Program je, u radnom vremenu, dok su inženjeri bili tu, nasumično gasio servere u stvarnom sistemu.
Ako bi gašenje jednog servera nešto pokvarilo, tim bi to vidio odmah, popravio, i sistem bi postao otporniji. Kasnije su napravili cijelu „vojsku majmuna” (Simian Army) za druge vrste kvarova, a Chaos Monkey su 2012. objavili kao otvoreni kod, da ga koriste i drugi. Ideja je jednostavna i moćna: ono što se može provjeriti automatski, provjerava se stalno, a ne jednom pred isporuku. Čovjek ne bi mogao svaki dan ručno gasiti servere i bilježiti šta se desilo; program može, bez umora, iznova i iznova.
U poglavlju 3 testirali smo web stranicu DeliveryApp rukom: izvršili smo osam test slučajeva i istraživali prvu verziju. To je dobro za nove funkcije i upotrebljivost, ali ima granicu. Ako svaki put kad programer promijeni kod treba ručno izvršiti 40 testova, tim će ih brzo prestati izvršavati. Ovo poglavlje uči automatizovano testiranje: program koji testira program. Prvo vidimo kada se automatizacija isplati, i to izračunamo. Zatim piramidu testova i vrste alata. Na kraju pišemo prvi automatski test, prvo samim Pythonom, pa u alatu pytest, i pokrećemo ga nad obje verzije DeliveryApp. Na praktičnoj nastavi tabela iz poglavlja 1 postaje skup automatskih testova koji se izvrši za manje od sekunde.
assert).Nakon poglavlja možete:
objasniti šta je automatizovan test i od čega se sastoji; izračunati od kojeg izvršavanja se automatizacija isplati; navesti šta ima smisla, a šta nema smisla automatizovati; objasniti piramidu testova i obrazac „ledenog konusa”; nabrojati vrste alata za automatizaciju i primjer za svaku; navesti osobine dobrog automatskog testa (FIRST); napisati provjeru u Pythonu naredbom assert; napisati, pokrenuti i pročitati izlaz testova u pytestu; pokrenuti iste testove nad obje verzije DeliveryApp i objasniti ulogu regresione svite.
| Čas | Sadržaj | Na času |
|---|---|---|
| Teorija 1 | 4.1–4.3 Automatski test; isplativost; šta ne automatizovati | Od 7. izvršavanja automatizacija je jeftinija |
| Teorija 2 | 4.4–4.6 Piramida; alati; osobine dobrog testa | Piramida i ledeni konus |
| Teorija 3 | 4.7–4.9 assert; pytest; regresija | 27 testova za desetinku sekunde; 8 padova na v0 |
Automatizovan test je mali program koji uradi ono što tester radi rukom, ali bez čovjeka:
ukupno(...));U Pythonu, najjednostavniji automatski test je jedan red:
assert ukupno([(950, 2), (250, 1)], 4.2) == 2450Naredba assert (engleski „tvrdim”) provjerava da je izraz iza nje tačan. Ako jeste, ništa se ne desi i program ide dalje. Ako nije, program staje s greškom AssertionError. To je sve: test je tvrdnja o tome kako sistem mora raditi, napisana tako da je računar može provjeriti.
Automatizovani testovi se ne pišu svaki za sebe kao poseban program. Koristi se okvir za testiranje (test framework): alat koji sam pronađe sve testove u projektu, pokrene ih, izmjeri vrijeme i napiše uredan izvještaj. Za Python je to najčešće pytest, za Javu JUnit, za C# NUnit ili xUnit, za JavaScript Jest ili Vitest. Ideja je u svima ista.
Slika iz života: automatski test je kao mjerač pritiska u autu koji se sam uključi svaki put kad upalite motor i zasvijetli ako je pritisak u gumama nizak. Vi ne morate svaki dan izaći i mjeriti gume; mjerač to radi umjesto vas, uvijek na isti način.
Automatski test nije besplatan. Treba ga napisati (vrijeme programera ili testera) i održavati (kad se sistem promijeni, i test se mijenja). Zauzvrat, izvršavanje ne košta ništa ljudskog vremena. Isplati li se?
Primjer: tim ima 40 testova cijene DeliveryApp. Ručno, svaki traje oko 2 minute, pa jedno izvršavanje svih traje 40 × 2 = 80 minuta. Automatizacija svih 40 traje 8 sati (480 minuta), a održavanje oko 10 minuta po izvršavanju (popravka testa kad se nešto promijeni). Poslije n izvršavanja:
Čemu služi. Formule porede ukupno ljudsko vrijeme. Automatizacija postaje jeftinija kad je 480 + 10n < 80n, odnosno 70n > 480, n > 6,9. Dakle, od sedmog izvršavanja automatski testovi štede vrijeme. Poslije 10 izvršavanja: ručno 800 minuta (preko 13 sati), automatski 580 minuta. A u stvarnosti se automatski testovi ne pokreću deset puta, nego pri svakoj izmjeni koda: ako tim napravi 5 izmjena dnevno, za dvije sedmice (10 radnih dana) to je 50 izvršavanja, ručno 4000 minuta (oko 67 sati), automatski 980 minuta. Ručno to niko ne bi ni uradio; testovi bi se jednostavno preskakali.
Brojevi u ovom primjeru su izmišljeni, ali pravilo je opšte: automatizacija se isplati za testove koji se izvršavaju često, a ne isplati za test koji će se izvršiti jednom ili dvaput.

Automatizacija nije uvijek dobar izbor. Ne automatizuje se:
I obrnuto, uvijek se automatizuje:
„Kad sve automatizujemo, ne trebaju nam testeri.” Automatski test provjerava samo ono što je neko smislio i napisao. Smišljanje dobrih testova, istraživanje i procjena kvaliteta ostaju ljudski posao. Automatizacija oslobađa testera od ponavljanja da bi imao vremena za razmišljanje.
Odgovori: tipka R ili klik na zamućeno polje.
Automatski testovi postoje na svim nivoima iz poglavlja 2. Koliko kojih? Odgovor daje piramida testova, koju je popularizovao Mike Cohn u knjizi o agilnom razvoju iz 2009. godine:
Za DeliveryApp bi to moglo izgledati ovako:
| Sloj | Primjeri | Broj | Trajanje svih |
|---|---|---|---|
| kroz ekran | Playwright: N-1001 kroz web stranicu, otkazivanje | 5–10 | nekoliko sekundi |
| API i integracija | POST /v1/cijena, API + baza | 20–40 | ispod sekunde do par sekundi |
| jedinični | cijena_dostave, popust, Narudzba.promijeni | 100+ | ispod sekunde |
Zašto ne obrnuto? Testovi kroz ekran su spori, a kad padnu, ne kaže se jasno šta je pokvareno: da li pravilo, API, baza ili samo pomjereno dugme. Uz to su često nestabilni (flaky): ponekad padnu jer je stranica sporo učitana, a ne zbog defekta. Tim koji ima mnogo testova kroz ekran, a malo jediničnih, ima piramidu naopako, koju zovu ledeni konus (kao kornet sladoleda): testovi traju satima, često padaju bez razloga, i ljudi im prestanu vjerovati.
Pravilo: svako pravilo se testira na najnižem nivou na kojem se može provjeriti. Granica od 30,00 € testira se jediničnim testom funkcije cijena_dostave, a ne kroz web stranicu. Kroz web stranicu testira se samo da je ta funkcija zaista povezana s ekranom.

Alati za automatizaciju dijele se po tome šta testiraju. Imena se brzo mijenjaju, ali vrste ostaju:
| Vrsta | Šta radi | Primjeri | U knjizi |
|---|---|---|---|
| okvir za jedinične testove | pokreće testove funkcija i klasa | pytest (Python), JUnit (Java), NUnit/xUnit (C#), Jest (JavaScript) | 4, 5, 11 |
| mjerenje pokrivenosti | pokazuje koji dio koda su testovi izvršili | coverage.py, JaCoCo | 6 |
| statička analiza | čita kod i nalazi sumnjiva mjesta bez pokretanja | ruff, pylint, mypy, SonarQube | 7 |
| testiranje API-ja | šalje HTTP zahtjeve i provjerava odgovore | httpx, requests, Postman, HTTPie | 9 |
| testiranje kroz preglednik | upravlja preglednikom kao korisnik | Playwright, Selenium, Cypress | 13 |
| performanse i opterećenje | glumi mnogo istovremenih korisnika | Locust, k6, JMeter | 10 |
| test dvojnici | glume dijelove sistema koje ne želimo zvati | unittest.mock, pytest-mock | 9, 11 |
| kontinuirana integracija | pokreće sve testove pri svakoj izmjeni | GitHub Actions, GitLab CI, Jenkins | 14 |
| upravljanje testovima | vodi test slučajeve, izvršavanja i defekte | Jira s dodacima, TestRail, Xray | 14 |
Posebna vrsta su alati snimi-i-pusti (record and playback): tester klikće po aplikaciji, alat snima klikove i kasnije ih ponavlja. Zvuči idealno, ali takvi testovi se lako pokvare: čim se dugme pomjeri ili preimenuje, snimak više ne radi. Danas se snimanje koristi kao početak (Playwright ima generator koda koji snimi klikove i napiše kod), a test se zatim doradi rukom: dobri lokatori, jasne provjere, bez nepotrebnih koraka.
Loš automatski test je gori od nikakvog: troši vrijeme, padne bez razloga i nauči tim da testovima ne vjeruje. Dobar automatski test ima pet osobina, koje se pamte po engleskoj riječi FIRST:
| Slovo | Osobina | Značenje | Primjer u DeliveryApp |
|---|---|---|---|
| F | brz (fast) | izvršava se u milisekundama, pa ga niko ne preskače | jedinični test cijene, a ne cijela web stranica |
| I | nezavisan (independent) | ne zavisi od drugih testova ni od redoslijeda | svaki test pravi svoju narudžbu |
| R | ponovljiv (repeatable) | daje isti rezultat svaki put, na svakom računaru | ne zavisi od današnjeg datuma, interneta ni slučajnih brojeva |
| S | sam se provjerava (self-validating) | sam kaže prošao ili pao; ne treba čovjek da gleda ispis | assert, a ne print i gledanje |
| T | pravovremen (timely) | piše se uz kod, a ne mjesecima kasnije | test za PP-5 uz kod popusta |
Najčešća povreda je R. Test koji provjerava „vrijeme dostave je ispod 30 minuta” prolazi danas, a pada u petak u podne kad je gužva; test koji zove pravi servis mapa pada kad internet zakaže. Takvi testovi su nestabilni i u poglavlju 12 vidimo kako se liječe. Povreda S je također česta kod početnika: test koji samo ispiše print(ukupno(...)) nije test, jer čovjek mora pročitati broj i znati da li je dobar.
Odgovori: tipka R ili klik na zamućeno polje.
Tabela očekivanih rezultata iz poglavlja 1 može se direktno pretvoriti u program. Svaki red postaje jedna tvrdnja:
from deliveryapp import cijena_dostave, ukupno
slucajevi = [
# (opis, km, artikli u centima, očekivana dostava u centima)
("T-01 do 3 km", 2, 1000, 200),
("T-02 tačno 3 km", 3, 1000, 200),
("T-03 3,1 km", 3.1, 1000, 250),
("T-04 4,2 km (N-1001)", 4.2, 2150, 300),
("T-05 tačno 10 km", 10, 1000, 550),
("T-07 tačno 30,00 €", 5, 3000, 0),
("T-08 29,99 €", 5, 2999, 300),
]
for opis, km, iznos, ocekivano in slucajevi:
stvarno = cijena_dostave(km, iznos)
assert stvarno == ocekivano, f"{opis}: očekivano {ocekivano}, stvarno {stvarno}"
print("sve provjere prošle")Iza zareza u assert stoji poruka koja se ispiše ako tvrdnja padne. Dobra poruka kaže koji slučaj, šta je očekivano i šta je stvarno, isto kao izvještaj o defektu iz poglavlja 3.
Ovaj program je već automatski test. Ali ima dvije mane: staje na prvoj greški (ostale slučajeve ne provjeri), i ne pravi uredan izvještaj. Obje rješava okvir za testiranje.
pytest je najpopularniji okvir za testiranje u Pythonu. Instalira se jednom naredbom (pip install pytest) i radi po jednostavnim dogovorima:
test_ (npr. test_prvi.py);test_;assert.Isti testovi kao u 4.7, u pytestu:
from deliveryapp import cijena_dostave, ukupno
def test_n1001_ukupno():
assert ukupno([(950, 2), (250, 1)], 4.2) == 2450
def test_tacno_3_km():
assert cijena_dostave(3, 1000) == 200
def test_3_1_km():
assert cijena_dostave(3.1, 1000) == 250
def test_besplatna_od_30_eura():
assert cijena_dostave(5, 3000) == 0
def test_29_99_eura():
assert cijena_dostave(5, 2999) == 300U terminalu, u folderu s fajlom, pokrećemo:
pytest -vpytest sam pronađe sve funkcije test_…, izvrši ih sve (i ako neka padne) i napiše izvještaj. Nad ispravnom verzijom DeliveryApp:
test_prvi.py::test_n1001_ukupno PASSED
test_prvi.py::test_tacno_3_km PASSED
test_prvi.py::test_3_1_km PASSED
test_prvi.py::test_besplatna_od_30_eura PASSED
test_prvi.py::test_29_99_eura PASSED
5 passed in 0.02sBez opcije -v (verbose, opširno) pytest za svaki test ispiše samo jedan znak: . prošao, F pao (failed: tvrdnja nije tačna), E greška (error: test se nije ni mogao izvršiti, npr. pogrešno ime funkcije).
Kad test padne, pytest pokaže tačno šta je dobio, iako u assert nismo napisali poruku. Na primjer, kad bismo testirali prvu verziju DeliveryApp:
def test_n1001_ukupno():
> assert ukupno([(950, 2), (250, 1)], 4.2) == 2450
E assert 2400 == 2450Ovo je pytestova posebna snaga: razloži tvrdnju i ispiše obje strane. Odmah vidimo da je ukupno za N-1001 2400 centi (24,00 €) umjesto 2450: dostava je 2,50 € umjesto 3,00 €. Defekt je negdje u obračunu dostave, a test ne kaže gdje. To je posao debagovanja, ali test je suzio traženje na jednu funkciju.
Kad testovi postoje, mogu se pokretati uvijek: poslije svake izmjene koda, kod svakog programera, na serveru za kontinuiranu integraciju. Takav skup testova zove se regresiona svita, i ona je mreža za sigurnost ispod akrobata: programer smije mijenjati kod hrabro, jer će svita uhvatiti ako je pokvario nešto što je ranije radilo.
U folderu primjeri/deliveryapp/tests/ nalazi se svita od 27 testova pravila DeliveryApp (fajl test_granice.py). Isti testovi mogu se pokrenuti nad obje verzije koda: fajl conftest.py u istom folderu, kad je postavljena promjenljiva okruženja DeliveryApp=v0, umjesto ispravnog modula deliveryapp učita prvu verziju deliveryapp_v0.
pytest tests/test_granice.py # ispravna verzija
27 passed in 0.13s
set DeliveryApp=v0 # u Windows Command Promptu; u PowerShellu $env:DeliveryApp="v0"; u Linuxu export DeliveryApp=v0
pytest tests/test_granice.py # prva verzija
8 failed, 19 passed in 0.07sOsam testova pada na prvoj verziji. Koji su to testovi i koje defekte otkrivaju, analiziraćemo u poglavlju 8. Za sada je važna lekcija: ista svita, ista sekunda, dva potpuno različita rezultata. Kad programeri isprave prvu verziju, ista svita će reći da li su ispravili sve, i da li su usput pokvarili nešto drugo.
Velike firme danas imaju stotine hiljada automatskih testova koji se pokreću hiljadama puta dnevno. Razlog nije da vole testove, nego da bez njih ne bi mogle mijenjati kod brzo: niko ne bi smio dirati sistem koji koriste milioni ljudi bez mreže za sigurnost.
Odgovori: tipka R ili klik na zamućeno polje.
Cilj je da tabela očekivanih rezultata iz poglavlja 1 postane automatski test u pytestu, da se pokrene nad obje verzije DeliveryApp i da se izmjeri koliko se štedi. Radi se u repozitoriju deliveryapp-testovi.
pip install pytest. Provjerite: pytest --version.deliveryapp.py, deliveryapp_v0.py i folder tests/ iz primjeri/deliveryapp/.prva_provjera.py po uzoru na 4.7, sa svim redovima vaše tabele iz ocekivano.md (poglavlje 1) koji su o cijeni dostave i ukupnom iznosu.python prva_provjera.py. Mora ispisati „sve provjere prošle”.from deliveryapp_v0 import … i pokrenite ponovo. Na kojem slučaju je program stao? Zašto ostale nije provjerio?tests/test_prvi.py i svaki red tabele pretvorite u posebnu funkciju test_… s jasnim imenom (npr. test_tacno_30_eura_besplatno).pytest -v tests/test_prvi.py. Svi testovi moraju proći nad ispravnom verzijom.DeliveryApp=v0, kako je opisano u 4.9). Zapišite koje funkcije padaju i šta pytest kaže da je dobio (red s E assert …).pytest -v tests/test_granice.py (gotova svita iz primjeri/). Zapišite broj testova i vrijeme.U ispravnoj verziji deliveryapp.py namjerno napravite jednu sitnu grešku (npr. MAX_KM = 11 ili PO_KM = 40) i pokrenite svitu. Koji testovi padaju? Da li bi vaš test_prvi.py sam otkrio tu grešku? Vratite ispravku i provjerite da je opet sve zeleno. Ovo je regresiono testiranje u malom.
Izmjerite štopericom koliko vam traje ručno izvršavanje 10 testova iz tabele (na Python konzoli ili web stranici), i koliko vam je trajalo pisanje test_prvi.py. Izračunajte, po formulama iz 4.2, od kojeg izvršavanja se automatizacija vama isplatila.
Dajte AI asistentu svoju tabelu iz ocekivano.md i zatražite da je pretvori u pytest testove. Pokrenite ih nad ispravnom verzijom. Ako neki pada nad ispravnom verzijom, ko je pogriješio: AI (pogrešan očekivani rezultat ili pogrešni centi) ili kod? Zapišite primjer.
Rezultat koji se predaje: prva_provjera.py, tests/test_prvi.py, i fajl rezultati-4.md s izlazom pytesta nad obje verzije (broj prošlih i palih, imena palih testova), rezultatom koraka 5 i računicom iz koraka 6.
Rješenja su sakrivena – tipka R ili klik na okvir.
Ručno izvršavanje 25 testova traje po 3 minute. Automatizacija svih traje 6 sati, a održavanje 15 minuta po izvršavanju. Od kojeg izvršavanja se automatizacija isplati?
Ručno: 25 × 3 = 75 minuta po izvršavanju. Automatski: 360 + 15n. Isplati se kad je 360 + 15n < 75n, odnosno 60n > 360, n > 6. Dakle od sedmog izvršavanja (kod šestog su jednaki: 450 = 450).
Za svaki test odlučite: automatizovati ili ručno? a) popust STUDENT10 na 50 iznosa; b) da li je nova boja dugmeta „Naruči” prijatna; c) jednokratni prenos 3000 starih narudžbi u novu bazu; d) dim-test poslije svake isporuke; e) istraživanje nove funkcije „zakaži za sutra”.
a) automatizovati (mnogo kombinacija, regresija); b) ručno (procjena izgleda); c) ručno ili jednokratnom skriptom koja se ne održava kao test (provjera prenosa); d) automatizovati; e) ručno (istraživanje), a nalaze kasnije pretvoriti u automatske testove.
Tim ima 200 testova kroz web stranicu, 20 API testova i 15 jediničnih testova. Cijela svita traje 2 sata i svaki dan nekoliko testova padne „bez razloga”. Šta je problem i šta biste predložili?
To je ledeni konus: previše sporih i krhkih testova kroz ekran, premalo jediničnih. Prijedlog: pravila (cijena, popust, stanja) prebaciti u jedinične testove, povezivanje provjeriti API testovima, a kroz ekran ostaviti desetak testova najvažnijih tokova. Nestabilne testove istražiti i popraviti (poglavlje 12) ili privremeno izdvojiti.
Koje slovo FIRST krši svaki test? a) test šalje zahtjev pravom servisu mapa na internetu; b) test test_otkazi radi samo ako je prije njega pokrenut test_napravi_narudzbu; c) test ispisuje ukupnu cijenu, a tester gleda da li je 24,50; d) test traje 40 sekundi jer svaki put pokreće cijeli preglednik za provjeru funkcije popusta.
a) R (ponovljiv: zavisi od interneta i servisa); b) I (nezavisan); c) S (sam se provjerava); d) F (brz); test popusta treba biti jedinični.
pytest je ispisao: ..F.E i ispod toga E assert 200 == 250 za treći test, te NameError: name 'cijena_dostav' is not defined za peti. Šta se desilo sa svakim od pet testova?
Prvi, drugi i četvrti su prošli (tačke). Treći je pao (F): funkcija je vratila 200, a očekivano je 250, što liči na dostavu za 3,1 km bez započetog kilometra. Peti je greška (E): test se nije mogao izvršiti jer je ime funkcije pogrešno napisano (cijena_dostav); to je greška u testu, a ne u sistemu.