✦ Srednji nivo Pythona i OOP Kvalitet koda

L2 · Koncept 05 · Kvalitet koda i testiranje

Testiranje klasa i izuzetaka
Proveri ponašanje objekata i nauči da testiraš očekivane greške.

Proveri kako se objekti menjaju tokom vremena i nauči kada je očekivani izuzetak dokaz da pravilo radi.

Cilj učenjaTestiraj stanje objekta, koristi setUp() za sveže test podatke i assertRaises() da proveriš očekivane izuzetke bez skrivanja neočekivanih grešaka.
01 Uči02 Radi lokalno03 Proveri razumevanje

Učenje kao gost

Učiš kao gost — sačuvaj svoj napredak!

Napravi besplatan nalog i uči uz Sloby AI mentora, proveru razumevanja i praćenje napretka.

Cela platforma je besplatna tokom 2026. Foundations ostaje zauvek besplatan.

Objekti imaju stanje koje vredi testirati.

Funkcija prima podatke i vraća rezultat. Objekat takođe ima stanje: njegove metode mogu menjati atribute tokom vremena. Zato koristan test često proverava stanje pre i posle operacije, a ne samo povratnu vrednost.

class Brojac:
    def __init__(self):
        self.vrednost = 0

    def povecaj(self):
        self.vrednost += 1


import unittest

class TestBrojac(unittest.TestCase):
    def test_povecanje(self):
        brojac = Brojac()

        brojac.povecaj()

        self.assertEqual(brojac.vrednost, 1)

Test pravi Brojac, poziva njegov metod, a zatim proverava izmenjeni atribut. Ponašanje koje nas zanima je stanje objekta: vrednost treba da postane 1.

Za klasu postavi tri korisna pitanja: Kakvo je početno stanje? Koje stanje treba da sledi nakon jedne operacije? Šta treba da se dogodi nakon druge operacije? Svaki test treba da napravi ili dobije stanje koje mu je potrebno, umesto da se oslanja na prethodni test.

↳

Testiraj pravilo, a ne samo poziv metoda: Metod može da se izvrši bez izuzetka, a ipak ostavi objekat u pogrešnom stanju. Provere čine očekivano pravilo vidljivim.

Početno i promenjeno stanje
class Korpa:
    def __init__(self):
        self.proizvodi = []

    def dodaj(self, proizvod: str):
        self.proizvodi.append(proizvod)


class TestKorpa(unittest.TestCase):
    def test_pocetno_stanje(self):
        korpa = Korpa()
        self.assertEqual(korpa.proizvodi, [])

    def test_dodavanje_dva_proizvoda(self):
        korpa = Korpa()
        korpa.dodaj("Knjiga")
        korpa.dodaj("Olovka")
        self.assertEqual(korpa.proizvodi, ["Knjiga", "Olovka"])

Ovi testovi koriste odvojene objekte. Jedan proverava praznu korpu, dok drugi proverava izmenjenu korpu. Nijednom testu nije potrebno da se drugi test prethodno pokrene.

setUp() svakom testu daje nov početak
import unittest

class TestKorpa(unittest.TestCase):
    def setUp(self):
        self.korpa = Korpa()

    def test_pocetno_stanje(self):
        self.assertEqual(self.korpa.proizvodi, [])

    def test_dodavanje(self):
        self.korpa.dodaj("Knjiga")
        self.assertEqual(self.korpa.proizvodi, ["Knjiga"])

setUp() se automatski pokreće pre svake test metode. To je pomoćni metod test klase, a ne konstruktor klase Korpa. Koristi ga kada je većem broju testova potrebna slična priprema; svaki test i dalje dobija svež objekat, zato redosled testova ne određuje ispravnost.

Očekivani izuzeci su deo pravila
def postavi_godine(godine: int) -> int:
    if godine < 0:
        raise ValueError("Godine ne mogu biti negativne.")
    return godine


class TestGodine(unittest.TestCase):
    def test_negativne_godine(self):
        with self.assertRaises(ValueError):
            postavi_godine(-5)

raise ValueError namerno odbija neispravnu vrednost. Unutar with self.assertRaises(ValueError) taj očekivani izuzetak čini da provera prođe. Ako se izuzetak ne pojavi, test daje FAIL. Ako se pojavi drugi, neočekivani izuzetak, test može dati ERROR. Oznake tipova opisuju očekivane tipove, ali ne proveravaju vrednosti tokom izvršavanja.

Proveri bezbedno stanje nakon odbijene operacije
class Novcanik:
    def __init__(self, stanje: float):
        self.stanje = stanje

    def potrosi(self, iznos: float):
        if iznos > self.stanje:
            raise ValueError("Nema dovoljno novca.")
        self.stanje -= iznos


def test_nedovoljno_sredstava(self):
    novcanik = Novcanik(100)

    with self.assertRaises(ValueError):
        novcanik.potrosi(150)

    self.assertEqual(novcanik.stanje, 100)

Ovaj test proverava dve odvojene činjenice: operacija podiže ValueError, a novčanik zadržava prethodno stanje. assertRaises() proverava samo izuzetak; kasniji assertEqual proverava stanje. Pravi novčanik bi proveravao i negativne iznose, ali ovaj mali primer ostaje fokusiran na nedovoljno novca.

Uobičajeni, rubni i neispravni ulazi
# Normal: deposit 100 RSD
racun.uplati(100)

# Edge: withdraw exactly the available balance
racun.isplati(racun.stanje)

# Invalid: withdraw more than the balance
with self.assertRaises(ValueError):
    racun.isplati(racun.stanje + 1)

Uobičajeni ulazi pokazuju redovno ponašanje. Rubni slučajevi proveravaju granicu, kao što je iznos tačno jednak stanju. Neispravni ulazi proveravaju da li klasa sledi jasno pravilo kada operacija mora biti odbijena.

Nezavisni testovi izbegavaju deljeno stanje
# Avoid one shared object changed by earlier tests
class TestRacun(unittest.TestCase):
    def setUp(self):
        self.racun = BankovniRacun(100)

    def test_uplata(self):
        self.racun.uplati(50)
        self.assertEqual(self.racun.stanje, 150)

Test treba da može da se pokrene samostalno. Nemoj koristiti jednu deljenu instancu čije stanje prethodni test menja. Sveža priprema olakšava razumevanje neuspešnog testa i sprečava skrivene zavisnosti od redosleda izvršavanja.

Poveži alate za testiranje koje poznaješ

  1. Jasno opiši pravila objektaOOP nam daje klase, objekte, metode i stanje. Oznake tipova i docstringovi pomažu da se objasni šta metod očekuje i šta obećava.
  2. Koristi unittest da zaštitiš ta pravilaProvere testiraju očekivano stanje i rezultate. Očekivani izuzeci testiraju namerno postavljene zaštite, umesto da se svaki podignuti izuzetak smatra ERROR rezultatom.
  3. Pažljivo istraži neuspešan testDebagovanje pomaže da pronađeš uzrok nakon što test prijavi problem. Zajedno, jasan kod i fokusirani testovi čine buduće održavanje bezbednijim.

Česte greške pri testiranju klasa

  • Deljenje jednog promenljivog objekta između testovaKoristi lokalni objekat ili setUp() da jedan test ne bi slučajno promenio početno stanje za drugi test.
  • Smatranje svakog izuzetka za ERROROčekivani ValueError može biti ispravno ponašanje kada pravilo odbija neispravan ulaz. assertRaises() mora navesti konkretan izuzetak koji očekuješ.
  • Provera samo da se izuzetak dogodioNakon odbijene operacije dodaj zasebnu proveru stanja objekta kada to stanje mora ostati bezbedno.
  • Pretpostavka da je setUp() konstruktor testirane klasesetUp() pripada test klasi i priprema svaki test. Ne zamenjuje __init__ u klasi koju testiraš.

Testiraj svoj bankovni račun

Lokalna vežba u VS Code-u

Preuzmi malu klasu bankovnog računa, pokreni njene početne testove, a zatim dodaj slučajeve za bezbedne promene stanja i odbijene operacije.

Preuzmi ZIP projekta
Fajlovi projekta
testing_classes_exceptions_bankovni_racun/
├── bankovni_racun.py
├── test_bankovni_racun.py
├── README_SR.md
└── README_EN.md

Početni projekat ima klasu BankovniRacun i četiri uspešna testa. Dodaj svoje uobičajene, rubne i neispravne slučajeve bez deljenja stanja između testova.

Testiraj račun lokalno

  1. Otvori raspakovani folder u VS Code-uPročitaj odgovarajući README fajl, a zatim pokreni početne testove u ugrađenom terminalu.
    python3 -m unittest -v
  2. Dodaj jedno pravilo po jednoZa neispravnu operaciju koristi jednu proveru za ValueError i drugu proveru da potvrdiš da stanje računa nije promenjeno.
↳

Pokreni ponovo nakon fokusirane izmene: python3 -m unittest -v Na sistemima gde se Python poziva kao <code>python</code>, koristi <code>python -m unittest -v</code>. Sačuvaj testove nakon svakog dodatka da bi mogli da štite pravila računa.

Bezbedna navika pri testiranju

  • Ne skrivaj neočekivanu greškuKoristi assertRaises() za konkretan očekivani izuzetak, a zatim istraži svaki drugačiji izuzetak ili ERROR rezultat.
↳

Ovim se završava modul o kvalitetu koda i testiranju: dizajn objekata, jasni tipovi i dokumentacija, debagovanje i automatski testovi zajedno čine Python kod lakšim za razumevanje, bezbednijim za menjanje i lakšim za održavanje.

Kakav će biti rezultat ovog unittest testa?

Pretpostavi da unittest pronalazi i pokreće ovaj test metod.

import unittest


def proveri_iznos(iznos):
    if iznos <= 0:
        raise ValueError("Amount must be positive.")


class TestIznos(unittest.TestCase):
    def test_negativan_iznos(self):
        with self.assertRaises(ValueError):
            proveri_iznos(-10)
Izaberi izlaz

Izgrađuj samopouzdanje samostalnim radom

Isplaniraj tri testa za objekat Temperatura

Usredsredi se na ponašanje <code>postavi()</code>. Jednostavni konstruktor u ovom primeru ne proverava početnu temperaturu.

Vaš ciljPredloži tri testa: početno stanje, uspešnu promenu temperature i vrednost ispod apsolutne nule koja podiže ValueError bez promene prethodnog stanja.
class Temperatura:
    def __init__(self, stepeni: float):
        self.stepeni = stepeni

    def postavi(self, nova_vrednost: float):
        if nova_vrednost < -273.15:
            raise ValueError("Temperatura ispod apsolutne nule.")
        self.stepeni = nova_vrednost

Radi u svom lokalnom VS Code folderu

Nagoveštaji korak po korak
  • Počni sa svežim objektom Temperatura i navedi njegovu očekivanu početnu vrednost.
  • Nakon ispravnog poziva postavi(), uporedi stepeni sa novom vrednošću.
  • Nakon assertRaises(ValueError), dodaj zasebnu proveru da stepeni i dalje ima prethodnu vrednost.

Pitaj Slobyja jedno vodeće pitanje o stanju, setUp() ili očekivanom izuzetku; nemoj tražiti da napiše tri završena testa umesto tebe.

Sloby, maskota Python AI Tutora

Tvoj mentor za učenje

Prijavite se da pitate Slobyja

Napravite besplatan nalog ili se prijavite za AI smernice tokom učenja.

Prijavite se da proverite svoje razumevanje

Napravite besplatan nalog ili se prijavite da biste dobili AI povratnu informaciju i sačuvali napredak u učenju.

Python AI Tutor · Uči razumevanjem, a ne samo pokretanjem koda.