Automaattestimine
Sissejuhatus
Tänapäevased projektid koosnevad mitmest osast. Need osad töötavad kooskõlas ja kui mingi osa teeb oma tööd valesti, siis võib olla terve süsteemi töö häiritud. Kui süsteemi täiendatakse või parandatakse, tuleb kindlaks teha, et ei tekkinud uusi vigu. Süsteemi töökindluse tagamiseks tuleb seda testida.
Üksuste testimine (Unit Testing)
Üksuste testimise puhul testitakse ühe konkreetset üksust koodist. Üksus võib olla nii funktsioon kui ka terve funktsioonidest (meetoditest) koosnev objekt. Objektide puhul testitakse seal olevaid meetodeid ja vaadatakse, et need õigesti mõjutaksid objekti. Funktsioonide puhul testitakse, et see annaks õige väljundi erinevate argumentide korral. Selleks, et programmeerija ei peaks kogu aeg funktsioone käivitama ja tulemust vaatama (näiteks print()
lausetega), kasutatakse testimise tööriistu. Meie räägime kahest sellisest: unittest ja py.test.
Kuidas kirjutada teste
Testide kirjutamiseks tuleb eelkõige kindlaks teha, mida peab testitav funktsioon tegema ja mida see peab erinevate argumentide korral tagastama. Siis tuleb välja mõelda erinevaid argumentide kombinatsioone, mis võivad võimalikult palju vigu tuvastada.
Proovime välja mõelda natuke teste ühe funktsiooni jaoks. Olgu meil on funktsioon ruutvõrrandi (ax^2 + bx + c = 0) lahendamiseks. See võtab ette 3 argumenti (a, b, c) ja tagastab listi. Kui lahendusi ei ole, siis list peab olema tühi. Vastasel juhul listis on lahendid suvalises järjekorras.
def quadratic_solution(a, b, c):
...
See on kõik, mida testijal on vaja teada, et funktsiooni testida. Mida võib siin testida? Tuleb lihtsalt proovida erinevaid argumente ja kontrollida vastust.
Erinevad a, b, c. (positiivsed, negatiivsed)
Kui a/b/c on 0 või nad on kõik nullid.
Kas on lahendid või ei ole.
Üksustestidega ei saa alati katta kõike võimalikke olukordi. On oluline, et kõige olulisemad olukorrad oleksid kaetud.
Testid kirjutatakse tavaliselt eraldi moodulisse (faili). See moodul koosneb funktsioonidest ja iga funktsioon testib ühte olukorda.
Testid peavad olema võimalikult lihtsad ja väga konkreetsed.
assert lause
assert
lausega kontrollitakse alamprogrammi (näiteks funktsiooni) töötamise korrektsust. Kui väljund on õige, siis Pythoni interpretaator jätkab koodi käivitamist, ja kui ei ole õige, siis viskab AssertionError erindi.
assert [boolean expression]
# boolean expression is an expression that returns True/False.
# i.e. True, False, 5 > 3, 5 <= 3, a == b, z in [5, 6, 1]
assert -1 in quadratic_solution(5, 6, 1)
assert len(quadratic_solution(-8, 1, -10)) == 0
assert 45 > 107 # throws AssertionError, because 45 > 107 is always False
py.test
Seaded PyCharm'is:
File => Settings (Ctrl + Alt + S) => Tools => Project: [projekti nimi] => Project Interpreter => Install(roheline + märk) => Otsi "pytest" => Install Package
File => Settings (Ctrl + Alt + S) => Tools => Python Integrated Tools => Default Test runner: py.test
Testide kirjutamiseks tasub luua eraldi moodul, nt. ttumath_tests.py. py.test käivitab automaatselt vaid neid funktsioone, mille nime algus on "test". Argumente testi funktsioonile ei määrata. Üksustesti kirjutamiseks tuleb luua funktsioon ja sinna panna mingi assert lause.
def test_base_arguments():
assert -1 in quadratic_solution(5, 6, 1)
Funktsioonis võib olla rohkem kui 1 assert lause, aga see ei ole alati parim lahendus. Kui test ebaõnnestub ja see koosneb mitmest osast, siis me ei saa kergelt teada, mis osa ebaõnnestub. Parim variant on see, et üks funktsioon testib ühte olukorda.
from ttumath import quadratic_solution
def test_base_arguments():
assert -1 in quadratic_solution(5, 6, 1)
def test_no_solutions():
assert len(quadratic_solution(-8, 1, -10)) == 0
Kui palju teste kirjutada on testija valik. Kuid mida rohkem on teste ja mida keerukam on testitav funktsioon, seda rohkem aega testimine võtab.
Kui testid on valmis, siis on aeg need käivitada. Menüüs: Run (Alt+Shift+F10) => 'py.test in ...'
Allpool ilmub aken, kus on info kõikidest testidest ja nende staatus (õnnestub/ei õnnestu).
Pytesti installimine käsurealt
pip install -U pytest
Unittest
Seaded PyCharm'is: File => Settings (Ctrl + Alt + S) => Tools => Python Integrated Tools => Default Test runner: Unittests
Testide kirjutamiseks tasub luua eraldi moodul, nt. math_tests.py. Funktsiooni nimi peab algama sõnaga "test" ja ta peab olema klassis, mis pärib klassi unittest.TestCase. (Tuleb importida moodul unittest) Klassi nimi võib olla suvaline.
import unittest
from foos import small_calculator
class MathTests(unittest.TestCase):
...
Nüüd aga funktsioonidel peab olema argument self, mis laseb kasutada superklassi funktsioone.
import unittest
from foos import small_calculator
class MathTests(unittest.TestCase):
def test_base_arguments(self):
assert -1 in quadratic_solution(5, 6, 1)
def test_no_solutions(self):
assert len(quadratic_solution(-8, 1, -10)) == 0
Unittest'i juhul on kombeks kasutada tavalise assert asemel järgmiseid funktsioone:
assertEqual(first, second) # if first != second, test fails
assertNotEqual(first, second) # if first == second, test fails
assertTrue(expression) # if expression == False, test fails
assertFalse(expression) # if expression == True, test fails
...
Read more: https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertEqual
Et neid funktsioone kasutada, tuleb pöörduda argumendi self juurde.
import unittest
from foos import small_calculator
class MathTests(unittest.TestCase):
def test_base_arguments(self):
self.assertTrue(-1 in quadratic_solution(5, 6, 1))
def test_no_solutions(self):
self.assertEqual(0, len(quadratic_solution(-8, 1, -10)))
Käivitamine: Menüüs: Run (Alt+Shift+F10) => 'Unittests for ...'
Kui test ebaõnnestub
Põhjused:
Kas funktsioon annab vale tulemuse
või test on valesti kirjutatu (nõuab valet vastust)
Esimene põhjus on programmeerija probleem, teine põhjus on testija probleem.
Edu!