Днес няма да си говорим за acceptance testing, quality assurance или нещо което се прави от "по-низшия" отдел във фирмата.
Проектът идва с готово задание, подробно задание. Прави се дизайн, с помоща на който работата се разбива на малки задачи. Те се извършват последователно, като не се налага да се връщате към някоя след като сте я приключили. Изискванията не се променят и не се налага добавянето на нова функционалност.
Щом съм написал един код, значи ми остава единствено да го разцъкам, да го пробвам в main
метода/функцията и толкова. Няма да се променя така или иначе, а ако пък случайно ми се наложи да го пипам - аз съм го писал, знам го, няма как да допусна грешка. Най-много да го поразцъкам още малко.
class Programmer(object):
...
def makeChange(self, project, change):
self.openAssociatedFiles(project, change)
while True:
self.attemptChange(change)
project.start()
result = self.clickAroundAndTest(project)
project.stop()
if result.sucessful(): break
self.commitCode(project)
self.hopeEverythingWentOK()
Това е автоматизирана задача. Всеки път повтаряме едно и също, отнема ни много време, уморява ни и има голям шанс да пропуснем нещо или да допуснем грешка. Защо вместо това не напишем малка програмка, която проверява дали кодът ни работи като хората? Така не просто ще имаме хубави критерии за кода, но и всеки друг ще може да проверява дали неговата промяна в модул А не е довела до грешка в нашия модул Б. Вместо да губим време да цъкаме всеки път, ще пускаме всички тестове автоматично и една команда, няма да въвеждаме никакви параметри и просто ще чакаме един от двата отгорова - "Всички тестове минаха усешно" или "Имаше грешка в тест Х".
class Interval(object):
def __init__(self, left, right):
self.left, self.right = left, right
def leftOpen(self): return self.left == None
def rightOpen(self): return self.right == None
def containsNumber(self, number):
if self.leftOpen() and self.rightOpen(): return true
if self.leftOpen(): return number <= self.right
if self.rightOpen(): return self.left <= number
return self.left < number < self.right
def __max(self, a, b): max(a, b) if not None in [a, b] else None
def __min(self, a, b): min(a, b) if not None in [a, b] else None
def insersect(self, other):
left = self.__max(self.left, other.left)
right = self.__max(self.right, other.right)
return Interval(left, right)
class IntervalTest:
def testContainsNumber(self):
interval = Interval(None, 0)
test("interval съдържа -3")
test("interval съдържа 0")
test("interval не съдържа 9")
test("interval.leftOpen() е истина")
test("interval.rightOpen() е лъжа")
def testIntersects(self):
test("сечението на [0, 10] със [5, None] е [5, 10]")
test("сечението на [None, 0] със [None, 42] е [None, 0]")
test("сечението на [None, 20] със [-20, None] е [-20, 20]")
class IntervalTest(unittest.TestCase):
def testContainsNumber(self):
interval = Interval(None, 0)
self.assertTrue(interval.containsNumber(-3))
self.assertTrue(interval.containsNumber(0))
self.failIf(interval.containsNumber(9))
self.assertTrue(interval.leftOpen())
self.failIf(interval.rightOpen())
def testIntersects(self):
self.assertEquals(
Interval(5, 10),
Interval(0, 10).intersect(Interval(5, None)))
self.assertEquals(
Interval(None, 0),
Interval(None, 42).intersect(Interval(None, 0)))
self.assertEquals(
Interval(-20, 20),
Interval(None, 20).intersect(Interval(-20, None)))
if __name__ == "__main__":
unittest.main()
.F
======================================================================
FAIL: testIntersects (__main__.GoodieListTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "./interval_test.py", line 16, in testCompact
self.assertEquals(
Interval(None, 0),
Interval(None, 42).intersects(Interval(None, 0)))
AssertionError: Interval(None, 0) != Interval(0, 0)
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (failures=1)
unittest.TestCase
test
и изпълнява поне една асертация.self.assert*
в тестовия метод.