Itt vagy: Kezdőlap ‣ Ugorj fejest a Python 3-ba ‣
Nehézségi szint: ♦♦♦♦♢
❝ Miután valaki rengeteg és még annál is több hangot játszott le, a művészet megkoronázásaként az egyszerűség emelkedik ki. ❞
– Frédéric Chopin
Akár tetszik, akár nem, előfordulnak programhibák. Az átfogó egységtesztek írására tett legnagyobb erőfeszítéseid ellenére, előfordulnak programhibák. Mit értek „programhiba” alatt? A programhiba egy olyan teszteset, amit még nem írtál meg.
>>> import roman7
>>> roman7.from_roman('') ①
0
InvalidRomanNumeralError
kivételt kellene dobnia, ahogyan minden, nem érvényes római számot képviselő karakterlánc esetén is.
A programhiba reprodukálása után, és javítása előtt írnod kell egy tesztesetet, amely sikertelenségével bemutatja a programhibát.
class FromRomanBadInput(unittest.TestCase):
.
.
.
def testBlank(self):
'''a from_roman nem engedélyezhet üres karakterláncot'''
self.assertRaises(roman6.InvalidRomanNumeralError, roman6.from_roman, '') ①
from_roman()
függvényt egy üres karakterlánccal, és győződjünk meg róla, hogy InvalidRomanNumeralError
kivételt dob. A nehéz feladat a programhiba megtalálása volt: most hogy tudsz róla, a tesztelés már könnyű.
Mivel a kódban jelen van a programhiba, és immár van egy ezt tesztelő teszteseted, a tesztelés sikertelen lesz:
you@localhost:~/diveintopython3/examples$ python3 romantest8.py -v a from_roman nem engedélyezhet üres karakterláncot ... FAIL a from_roman nem engedélyezhet rosszul formázott előtagokat ... ok a from_roman nem engedélyezhet ismétlődő számpárokat ... ok a from_roman nem engedélyezhet túl sok ismétlődő karaktert ... ok a from_roman ismert eredményt kell adjon ismert bemenetre ... ok a to_roman ismert eredményt kell adjon ismert bemenetre ... ok from_roman(to_roman(n))==n minden n-re ... ok a to_roman nem engedélyezhet negatív bemenetet ... ok a to_roman nem engedélyezhet nem egész bemenetet ... ok a to_roman nem engedélyezhet túl nagy bemenetet ... ok a to_roman nem engedélyezheti a 0 bemenetet ... ok ====================================================================== FAIL: a from_roman nem engedélyezhet üres karakterláncot ---------------------------------------------------------------------- Traceback (most recent call last): File "romantest8.py", line 117, in test_blank self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, '') AssertionError: InvalidRomanNumeralError not raised by from_roman ---------------------------------------------------------------------- Ran 11 tests in 0.171s FAILED (failures=1)
Most kijavíthatod a programhibát.
def from_roman(s):
'''római számok egésszé alakítása'''
if not s: ①
raise InvalidRomanNumeralError('A bemenet nem lehet üres')
if not re.search(romanNumeralPattern, s):
raise InvalidRomanNumeralError('Érvénytelen római szám: {}'.format(s)) ②
result = 0
index = 0
for numeral, integer in romanNumeralMap:
while s[index:index+len(numeral)] == numeral:
result += integer
index += len(numeral)
return result
raise
utasítás.
{0}
formátum-előírás helyett a format()
metódus első paraméterére egyszerűen a {}
használatával is hivatkozhatsz, és a Python kitölti helyetted a megfelelő pozicionális indexet. Ez tetszőleges számú argumentum esetén is működik: az első {}
jelentése {0}
, a második {}
jelentése {1}
, és így tovább.
you@localhost:~/diveintopython3/examples$ python3 romantest8.py -v a from_roman nem engedélyezhet üres karakterláncot ... ok ① a from_roman nem engedélyezhet rosszul formázott előtagokat ... ok a from_roman nem engedélyezhet ismétlődő számpárokat ... ok a from_roman nem engedélyezhet túl sok ismétlődő karaktert ... ok a from_roman ismert eredményt kell adjon ismert bemenetre ... ok a to_roman ismert eredményt kell adjon ismert bemenetre ... ok from_roman(to_roman(n))==n minden n-re ... ok a to_roman nem engedélyezhet negatív bemenetet ... ok a to_roman nem engedélyezhet nem egész bemenetet ... ok a to_roman nem engedélyezhet túl nagy bemenetet ... ok a to_roman nem engedélyezheti a 0 bemenetet ... ok ---------------------------------------------------------------------- Ran 11 tests in 0.156s OK ②
Ez a kódolási stílus nem teszi könnyebbé a programhibák javítását. Az egyszerű programhibák (mint ez) egyszerű teszteseteket, a bonyolult programhibák bonyolult teszteseteket igényelnek. Egy tesztelésközpontú környezetben úgy tűnhet, mintha tovább tartana egy programhiba javítása, mivel kódban kell kifejezned a programhibát (a teszteset megírásához), és csak ezután javíthatod ki magát a programhibát. Ezután ha a teszteset nem lesz azonnal sikeres, meg kell találnod, hogy a programhiba javítása volt-e rossz, vagy maga a teszteset lett-e hibás. Azonban hosszú távon ez az oda-vissza ugrálás a kód tesztelése és a tesztelt kód között kifizetődik, mert valószínűbbé teszi, hogy a programhibák már első alkalommal is helyesen kerültek kijavításra. Továbbá, mivel az újjal együtt egyszerűen újrafuttathatod az összes tesztesetet, sokkal kevésbé valószínű, hogy az új kód javítása során véletlenül elrontasz valami régi kódot. A ma egységtesztje a holnap regressziótesztje.
⁂
Legnagyobb erőfeszítéseid dacára, amelyek az ügyfeleid földhöz szögezésére és belőlük a pontos követelmények kiszedésére vonatkoznak, például ollók és forró viasz által okozott fájdalom útján, a követelmények változni fognak. A legtöbb ügyfél nem tudja mit akar, amíg nem látja azt; és még ha tudják is, nem különösebben jók a kívánságaik használható részletességgel való megfogalmazásában. Ha pedig mégis, akkor a következő kiadásban többet fognak akarni. Emiatt készülj fel a teszteseteid frissítésére a követelmények változásával együtt.
Tegyük fel például, hogy ki szeretnéd terjeszteni a római számokat átalakító függvények tartományát. Általában a római számokban egyik karakter sem ismétlődhet egymás után háromnál többször. De a rómaiak szívesen tettek kivételt ez alól a szabály alól, és megengedtek egymás után 4 M
karaktert a 4000
ábrázolásához. Ha elvégzed ezt a módosítást, akkor kiterjesztheted az átalakítható számok tartományát1..3999
-ről 1..4999
-re. De előbb a teszteseteidet kell módosítanod.
class KnownValues(unittest.TestCase):
known_values = ( (1, 'I'),
.
.
.
(3999, 'MMMCMXCIX'),
(4000, 'MMMM'), ①
(4500, 'MMMMD'),
(4888, 'MMMMDCCCLXXXVIII'),
(4999, 'MMMMCMXCIX') )
class ToRomanBadInput(unittest.TestCase):
def test_too_large(self):
'''a to_roman nem engedélyezhet túl nagy bemenetet'''
self.assertRaises(roman8.OutOfRangeError, roman8.to_roman, 5000) ②
.
.
.
class FromRomanBadInput(unittest.TestCase):
def test_too_many_repeated_numerals(self):
'''a from_roman nem engedélyezhet túl sok ismétlődő karaktert'''
for s in ('MMMMM', 'DD', 'CCCC', 'LL', 'XXXX', 'VV', 'IIII'): ③
self.assertRaises(roman8.InvalidRomanNumeralError, roman8.from_roman, s)
.
.
.
class RoundtripCheck(unittest.TestCase):
def test_roundtrip(self):
'''from_roman(to_roman(n))==n minden n-re'''
for integer in range(1, 5000): ④
numeral = roman8.to_roman(integer)
result = roman8.from_roman(numeral)
self.assertEqual(integer, result)
4000
-es tartományban. Itt felvettem a 4000
(a legrövidebb), a 4500
(a második legrövidebb), a 4888
(a leghosszabb) és a 4999
(a legnagyobb) értékeket.
to_roman()
függvényt a 4000
értékkel hívta, és hibát várt. Most hogy a 4000-4999
tartomány is jónak számít, ezt meg kell növelned 5000
-re.
from_roman()
függvényt az 'MMMM'
értékkel hívta, és hibát várt. Most hogy az MMMM
is érvényes római számnak számít, ezt meg kell növelned 'MMMMM'
-re.
1
és 3999
között. Mivel a tartomány kibővült, ezt a for
ciklust is frissítened kell a 4999
-ig haladáshoz.
Most már minden teszteseted az új követelményekhez van igazítva, de a kódod még nem, így valószínűleg több teszteset is sikertelen lesz.
you@localhost:~/diveintopython3/examples$ python3 romantest9.py -v a from_roman nem engedélyezhet üres karakterláncot ... ok a from_roman nem engedélyezhet rosszul formázott előtagokat ... ok a from_roman nem engedélyezhet nem karakterlánc bemenetet ... ok a from_roman nem engedélyezhet ismétlődő számpárokat ... ok a from_roman nem engedélyezhet túl sok ismétlődő karaktert ... ok a from_roman ismert eredményt kell adjon ismert bemenetre ... ERROR ① a to_roman ismert eredményt kell adjon ismert bemenetre ... ERROR ② from_roman(to_roman(n))==n minden n-re ... ERROR ③ a to_roman nem engedélyezhet negatív bemenetet ... ok a to_roman nem engedélyezhet nem egész bemenetet ... ok a to_roman nem engedélyezhet túl nagy bemenetet ... ok a to_roman nem engedélyezheti a 0 bemenetet ... ok ====================================================================== ERROR: a from_roman ismert eredményt kell adjon ismert bemenetre ---------------------------------------------------------------------- Traceback (most recent call last): File "romantest9.py", line 82, in test_from_roman_known_values result = roman9.from_roman(numeral) File "C:\home\diveintopython3\examples\roman9.py", line 60, in from_roman raise InvalidRomanNumeralError('Érvénytelen római szám: {0}'.format(s)) roman9.InvalidRomanNumeralError: Érvénytelen római szám: MMMM ====================================================================== ERROR: a to_roman ismert eredményt kell adjon ismert bemenetre ---------------------------------------------------------------------- Traceback (most recent call last): File "romantest9.py", line 76, in test_to_roman_known_values result = roman9.to_roman(integer) File "C:\home\diveintopython3\examples\roman9.py", line 42, in to_roman raise OutOfRangeError('a szám kívül esik a tartományon (0 és 3999 közt kell legyen)') roman9.OutOfRangeError: a szám kívül esik a tartományon (0 és 3999 közt kell legyen) ====================================================================== ERROR: from_roman(to_roman(n))==n minden n-re ---------------------------------------------------------------------- Traceback (most recent call last): File "romantest9.py", line 131, in testSanity numeral = roman9.to_roman(integer) File "C:\home\diveintopython3\examples\roman9.py", line 42, in to_roman raise OutOfRangeError('a szám kívül esik a tartományon (0 és 3999 közt kell legyen)') roman9.OutOfRangeError: a szám kívül esik a tartományon (0 és 3999 közt kell legyen) ---------------------------------------------------------------------- Ran 12 tests in 0.171s FAILED (errors=3)
from_roman()
ismert értékek tesztje az 'MMMM'
elérésekor azonnal sikertelenné válik, mert a from_roman()
ezt még mindig érvénytelen római számnak gondolja.
to_roman()
ismert értékek tesztje a 4000
elérésekor azonnal sikertelenné válik, mert a to_roman()
ezt még mindig tartományon kívülinek gondolja.
4000
elérésekor szintén sikertelenné válik, mert a to_roman()
ezt még mindig tartományon kívülinek gondolja.
Most, hogy vannak az új követelmények miatt sikertelen teszteseteid, kezdhetsz a kód javítására gondolni. (Amikor először elkezdesz egységteszteket írni, furcsának érezheted, hogy a tesztelt kód soha nem jár a tesztesetek „előtt”. Amíg mögöttük van, van tennivalód, és amint utoléri a teszteseteket, befejezed a kódolást. Miután hozzászoktál, azon fogsz csodálkozni, hogy korábban hogyan tudtál egyáltalán tesztek nélkül programozni.)
roman_numeral_pattern = re.compile('''
^ # karakterlánc eleje
M{0,4} # ezresek - 0 és 4 közti M ①
(CM|CD|D?C{0,3}) # százasok - 900 (CM), 400 (CD), 0-300 (0 és 3 közti C),
# vagy 500-800 (D, amelyet 0 és 3 közti C követ)
(XC|XL|L?X{0,3}) # tizesek - 90 (XC), 40 (XL), 0-30 (0 és 3 közti X),
# vagy 50-80 (L, amelyet 0 és 3 közti X követ)
(IX|IV|V?I{0,3}) # egyesek - 9 (IX), 4 (IV), 0-3 (0 és 3 közti I),
# vagy 5-8 (V, amelyet 0 és 3 közti I követ)
$ # karakterlánc vége
'''def to_roman(n):
'''egész szám római számmá alakítása'''
if not isinstance(n, int):
raise NotIntegerError('a nem egész számok nem alakíthatók át')
if not (0 < n < 5000): ②
raise OutOfRangeError('a szám kívül esik a tartományon (1 és 4999 közt kell legyen)')
result = ''
for numeral, integer in roman_numeral_map:
while n >= integer:
result += numeral
n -= integer
return result
def from_roman(s):
.
.
.
from_roman()
függvényt egyáltalán nem kell módosítanod. Az egyetlen változás a roman_numeral_pattern kifejezést érinti. Ha közelebbről nézed, akkor észre fogod venni, hogy a nem kötelező M
karakterek maximális számát megváltoztattam 3
-ról 4
-re a reguláris kifejezés első részében. Ez 4999
-ig teszi lehetővé a római számok használatát a 3999
helyett. A tényleges from_roman()
függvény teljesen általános, csupán ismétlődő rómaiszám-karaktereket keres, és összeadja azokat, nem törődve az ismétlések számával. Egyetlen oka volt annak, hogy nem kezelte az 'MMMM'
-et korábban: az, hogy kifejezetten megállítottad a reguláris kifejezés mintaillesztésénél.
to_roman()
függvény csak egy kis változtatást igényel a tartomány-ellenőrzésnél. A 0 < n < 4000
ellenőrzésénél mostantól a 0 < n < 5000
áll. Ezen kívül meg kell változtatnod a kivétel dobásánál a raise
utasítás hibaüzenetét, hogy az új dob tartományt tükrözze (1..4999
az 1..3999
helyett). A függvény többi részét nem kell módosítanod, az új eseteket már kezeli. (Boldogan hozzáadja az'M'
-et az összes megtalált ezreshez, a 4000
megadásakor pedig 'MMMM'
-et ad vissza. Az egyetlen ok, amiért ezt korábban nem tette, az az, hogy kifejezetten megállítottad a tartomány-ellenőrzéssel.)
Kételkedhetsz benne, hogy ez a két kis változtatás minden, amire szükséged van. Ne hidd el nekem, próbáld ki magad.
you@localhost:~/diveintopython3/examples$ python3 romantest9.py -v
a from_roman nem engedélyezhet üres karakterláncot ... ok
a from_roman nem engedélyezhet rosszul formázott előtagokat ... ok
a from_roman nem engedélyezhet nem karakterlánc bemenetet ... ok
a from_roman nem engedélyezhet ismétlődő számpárokat ... ok
a from_roman nem engedélyezhet túl sok ismétlődő karaktert ... ok
a from_roman ismert eredményt kell adjon ismert bemenetre ... ok
a to_roman ismert eredményt kell adjon ismert bemenetre ... ok
from_roman(to_roman(n))==n minden n-re ... ok
a to_roman nem engedélyezhet negatív bemenetet ... ok
a to_roman nem engedélyezhet nem egész bemenetet ... ok
a to_roman nem engedélyezhet túl nagy bemenetet ... ok
a to_roman nem engedélyezheti a 0 bemenetet ... ok
----------------------------------------------------------------------
Ran 12 tests in 0.203s
OK ①
Az átfogó egységtesztelés azt jelenti, hogy soha nem kell egy olyan programozóra hagyatkoznod, aki azt mondja: „Bízz bennem.”
⁂
Az átfogó egységtesztelés legjobb része nem az az érzés, amikor végre minden teszteseted átmegy, vagy akár az az érzés, amikor valaki téged okol a kódja elromlásáért, és be tudod bizonyítani, hogy nem te voltál. Az egységtesztelés legjobb része az, hogy szabadságot ad a kíméletlen átdolgozásra.
Az átdolgozás a működő kód jobban működővé tételét jelenti. Általában a „jobban” azt jelenti, hogy „gyorsabban”, noha jelentheti azt is, hogy „kevesebb memóriát használva”, vagy „kevesebb lemezterületet használva”, vagy egyszerűen csak „elegánsabban”. Akármit is jelent számodra, a projekted számára, a környezetedben, az átdolgozás bármely program hosszú távú egészsége szempontjából fontos.
Itt most a „jobban” egyszerre jelenti azt, hogy „gyorsabban” és hogy „egyszerűbben karbantartható”. Konkrétan a from_roman()
függvény lassabb és bonyolultabb, mint ahogy szeretném, mégpedig a római számok ellenőrzéséhez használt nagy, ronda reguláris kifejezés miatt. Most az juthat eszedbe: „Persze, a reguláris kifejezés nagy és ronda, de hogy máshogy ellenőrizhetném, hogy egy tetszőleges karakterlánc érvényes római szám-e?”
Válasz: csak 5000 van belőlük, miért nem készítesz egy kikeresési táblát? Ez az ötlet egyre jobbá válik, amikor rájössz, hogy egyáltalán nem kell reguláris kifejezéseket használnod. Az egészek római számokká alakításához használandó kikeresési tábla felépítése során felépítheted a fordított kikeresési táblát is a római számok egészekké alakításához. Mire ellenőrizned kell, hogy egy tetszőleges karakterlánc érvényes római szám-e, már összegyűjtötted az összes érvényes római számot. Az „ellenőrzés” egyetlen szótári kikereséssé egyszerűsödött.
A legjobb pedig, hogy már van egy teljes sorozat egységteszted. Megváltoztathatod a modul kódjának több, mint felét, de az egységtesztek ugyanazok maradnak. Ez azt jelenti, hogy bebizonyíthatod – magadnak és másoknak – is, hogy az új kód ugyanolyan jól működik, mint az eredeti.
class OutOfRangeError(ValueError): pass
class NotIntegerError(ValueError): pass
class InvalidRomanNumeralError(ValueError): pass
roman_numeral_map = (('M', 1000),
('CM', 900),
('D', 500),
('CD', 400),
('C', 100),
('XC', 90),
('L', 50),
('XL', 40),
('X', 10),
('IX', 9),
('V', 5),
('IV', 4),
('I', 1))
to_roman_table = [ None ]
from_roman_table = {}
def to_roman(n):
'''egész szám római számmá alakítása'''
if not (0 < n < 5000):
raise OutOfRangeError('a szám kívül esik a tartományon (1 és 4999 közt kell legyen)')
if int(n) != n:
raise NotIntegerError('a nem egész számok nem alakíthatók át')
return to_roman_table[n]
def from_roman(s):
'''római számok egésszé alakítása'''
if not isinstance(s, str):
raise InvalidRomanNumeralError('A bemenetnek karakterláncnak kell lennie')
if not s:
raise InvalidRomanNumeralError('A bemenet nem lehet üres')
if s not in from_roman_table:
raise InvalidRomanNumeralError('Érvénytelen római szám: {0}'.format(s))
return from_roman_table[s]
def build_lookup_tables():
def to_roman(n):
result = ''
for numeral, integer in roman_numeral_map:
if n >= integer:
result = numeral
n -= integer
break
if n > 0:
result += to_roman_table[n]
return result
for integer in range(1, 5000):
roman_numeral = to_roman(integer)
to_roman_table.append(roman_numeral)
from_roman_table[roman_numeral] = integer
build_lookup_tables()
Szedjük ezt szét emészthető darabokra. Vitathatalanul a legfontosabb sor az utolsó:
build_lookup_tables()
Észreveheted, hogy ez egy függvényhívás, de nincs körülötte if
utasítás. Ez nem egy if __name__ == '__main__'
blokk: a modul importálásakor kerül meghívásra. (Fontos megértened, hogy a modulok csak egyszer kerülnek importálásra, azután gyorsítótárazva lesznek. Ha egy már importált modult importálsz, akkor nem történik semmi. Így ez a kód csak a modul első importálásakor kerül meghívásra.)
Tehát mit csinál a build_lookup_tables()
függvény? Örülök, hogy megkérdezted!
to_roman_table = [ None ]
from_roman_table = {}
.
.
.
def build_lookup_tables():
def to_roman(n): ①
result = ''
for numeral, integer in roman_numeral_map:
if n >= integer:
result = numeral
n -= integer
break
if n > 0:
result += to_roman_table[n]
return result
for integer in range(1, 5000):
roman_numeral = to_roman(integer) ②
to_roman_table.append(roman_numeral) ③
from_roman_table[roman_numeral] = integer
to_roman()
függvény fentebb van definiálva: kikeresi az értékeket a táblában, és visszaadja azokat. De a build_lookup_tables()
függvény átdefiniálja a to_roman()
függvényt, hogy valójában csináljon is valamit (mint a korábbi példák, mielőtt elkezdtél volna kikeresési táblát használni). A build_lookup_tables()
függvényen belül a to_roman()
hívása ezt az átdefiniált verziót fogja meghívni. Amikor a build_lookup_tables()
függvény kilép, az átdefiniált verzió eltűnik – az csak a build_lookup_tables()
függvény helyi hatókörében van definiálva.
to_roman()
függvényt, amely valójában kiszámítja a római számot.
to_roman()
függvényből), felveszed az egészet és a római számos megfelelőjét mindkét kikeresési táblába.
Miután a kikeresési táblák felépültek, a kód többi része egyszerű és gyors is.
def to_roman(n):
'''egész szám római számmá alakítása'''
if not (0 < n < 5000):
raise OutOfRangeError('a szám kívül esik a tartományon (1 és 4999 közt kell legyen)')
if int(n) != n:
raise NotIntegerError('a nem egész számok nem alakíthatók át')
return to_roman_table[n] ①
def from_roman(s):
'''római számok egésszé alakítása'''
if not isinstance(s, str):
raise InvalidRomanNumeralError('A bemenetnek karakterláncnak kell lennie')
if not s:
raise InvalidRomanNumeralError('A bemenet nem lehet üres')
if s not in from_roman_table:
raise InvalidRomanNumeralError('Érvénytelen római szám: {0}'.format(s))
return from_roman_table[s] ②
to_roman()
függvény egyszerűen megkeresi a megfelelő értéket a kikeresési táblában, és visszaadja azt.
from_roman()
függvény is némi határellenőrzésre és egy sor kódra zsugorodott. Nincs több reguláris kifejezés. Nincs több ciklus. O(1) átalakítás római számokká és vissza.
De működik ez? Hát hogyne, persze hogy. Be tudom bizonyítani.
you@localhost:~/diveintopython3/examples$ python3 romantest10.py -v
a from_roman nem engedélyezhet üres karakterláncot ... ok
a from_roman nem engedélyezhet rosszul formázott előtagokat ... ok
a from_roman nem engedélyezhet nem karakterlánc bemenetet ... ok
a from_roman nem engedélyezhet ismétlődő számpárokat ... ok
a from_roman nem engedélyezhet túl sok ismétlődő karaktert ... ok
a from_roman ismert eredményt kell adjon ismert bemenetre ... ok
a to_roman ismert eredményt kell adjon ismert bemenetre ... ok
from_roman(to_roman(n))==n minden n-re ... ok
a to_roman nem engedélyezhet negatív bemenetet ... ok
a to_roman nem engedélyezhet nem egész bemenetet ... ok
a to_roman nem engedélyezhet túl nagy bemenetet ... ok
a to_roman nem engedélyezheti a 0 bemenetet ... ok
----------------------------------------------------------------------
Ran 12 tests in 0.031s ①
OK
to_roman()
és from_roman()
függvények összes hívása között. Mivel a tesztek több ezer függvényhívást végeznek (a körbejárás teszt magában 10000-et), ezek a megtakarítások gyorsan összeadódnak!
A történet tanulsága?
⁂
Az egységtesztelés egy hatékony alapelv, amelyet megfelelően megvalósítva egyszerre csökkentheti a karbantartási költségeket, és növelheti a rugalmasságot bármely hosszú távú projektben. Fontos megérteni azt is, hogy az egységtesztelés nem csodaszer, Bűvös Problémamegoldó, vagy mindent vivő Lola T. Jó tesztesetek írása nehéz, és a naprakészen tartásuk eltökéltséget igényel (különösen amikor az ügyfelek kritikus hibajavításokat akarnak tegnapra). Az egységtesztelés nem helyettesíti a tesztelés más formáit, beleértve a funkcionális tesztelést, integrációs tesztelést és a felhasználó általi elfogadás tesztelését. Azonban megvalósítható és működik, ha pedig egyszer már láttad működni, azt fogod kérdezni, hogyan tudtál eddig enélkül élni.
Ez a néhány fejezet rengeteg témát felölelt, és ebből sok nem is volt Python-specifikus. Sok nyelvhez léteznek egységtesztelő keretrendszerek, amelyek mindegyike ugyanazon alapvető fogalmak megértését igényli:
© 2001–11 Mark Pilgrim