Днес няма да си говорим за acceptance testing, quality assurance или нещо, което се прави от „по-низшия“ отдел във фирмата. Всичко тук е дело на програмиста.
Проектът идва с готово, подробно задание. Прави се дизайн. С него работата се разбива на малки задачи. Те се извършват последователно. За всяка от тях пишете кода и приключвате. Изискванията не се променят, нито се добавя нова функционалност.
Щом съм написал един код, значи ми остава единствено да го разцъкам - няколко print
-а, малко пробване в main
метода/функцията и толкова. Така или иначе няма да се променя. А ако (не дай си боже) това се случи - аз съм го писал, знам го, няма как да допусна грешка. Най-много да го поразцъкам още малко.
class Programmer(object): # ... def implement_a_change(self, project, change): files = self.open_related_files(project, change) while True: self.attempt_change(change, files) project.run() result = self.click_around_and_test(project) project.stop() if result.successful(): break self.commit_code(project, files) self.hope_everything_went_ok()
— Добре де… хващам се, че постоянно правя едно и също нещо като робот. Понеже е досадно, лесно ще забравя нещо. Пък и само ми губи времето. Човешката цивилизация не реши ли тоя вид проблеми с някакви машини? Май се казваха компютри?
— Защо просто не си напишеш програма, която да го прави вместо теб?
class Interval(object): def __init__(self, left, right): self.left, self.right = left, right def __repr__(self): return "Interval({0}, {1})".format(self.left, self.right) def __eq__(self, other): return isinstance(other, Interval) and \ (self.left, self.right) == (other.left, other.right) def left_open(self): return self.left == None def right_open(self): return self.right == None def contains_number(self, number): if self.left_open() and self.right_open(): return True if self.left_open(): return number <= self.right if self.right_open(): return self.left <= number return self.left < number < self.right def intersect(self, other): extr = lambda a, b, func: func(a, b) if not None in (a, b) else a or b return Interval( extr(self.left, other.left, max), extr(self.right, other.right, min)) __and__ = intersect
class IntervalTest: def test_contains_number(self): interval = Interval(None, 0) твърдя_че("interval съдържа -3") твърдя_че("interval съдържа 0") твърдя_че("interval не съдържа 9") твърдя_че("interval.left_open() е истина") твърдя_че("interval.right_open() е лъжа") def test_intersects(self): твърдя_че("сечението на [0, 10] с [5, None] е [5, 10]") твърдя_че("сечението на [None, 0] с [None, 42] е [None, 0]") твърдя_че("сечението на [None, 20] с [-20, None] е [-20, 20]") твърдя_че("сечението на [None, 0] с [-10, None] е [-10, 0]")
class IntervalTest(unittest.TestCase): def test_contains_number(self): interval = Interval(None, 0) self.assertTrue(interval.contains_number(-3)) self.assertTrue(interval.contains_number(0)) self.failIf(interval.contains_number(9)) self.assertTrue(interval.left_open()) self.failIf(interval.right_open()) def test_intersects(self): self.assertEquals( Interval(5, 10), Interval(0, 10) & Interval(5, None)) self.assertEquals( Interval(None, 0), Interval(None, 42) & Interval(None, 0)) self.assertEquals( Interval(-20, 20), Interval(None, 20) & Interval(-20, None)) self.assertEquals( Interval(-10, 0), Interval(None, 0) & Interval(-10, None)) if __name__ == "__main__": unittest.main()
.F ====================================================================== FAIL: test_intersects (__main__.IntervalTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "", line 52, in test_intersects AssertionError: Interval(-10, 0) != Interval(-10, None) ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1)
vocabulary = { "група": ("test case", unittest.TestCase), "сценарий": ("test method", [_ for _ in dir(YourTestCase) if _.startswith("test")]), "твърдение": ("assertion", [_ for _ in dir(unittest.TestCase) if re.match("assert|fail", _)]) }Важно. Не бъркайте ключовата дума
assert
с методите за твърдения в тестовете. Първото служи да прекратите програмата ако изпадне в невалидно състояние. Второто е част от библиотеката за тестове.
Всички методи имат опционален последен аргумент msg
- текстово съобщение, което ще се покаже ако теста пропадне.
self.assertTrue(expr)
- още assert_
и failUnless
self.assertFalse(expr)
- още failIf
self.assertEqual(expected, actual)
- още assertEquals
и failUnlessEqual
self.assertAlmostEqual(expected, actual, places=7)
- още assertAlmostEquals
и failUnlessAlmostEqual
self.assertNotAlmostEqual(expected, actual, places=7)
- още assertNotAlmostEquals
и failIfAlmostEqual
self.assertRaises(self, excClass, callable, *args, **kwargs)
- още failUnlessRaises
class SomeText(TestCase): @skip("This is not yet implemented") def test_something(self): ...
@skip(reason)
@skipIf(condition, reason)
@skipUnless(condition, reason)
@expectedFailure
setUp
и tearDown
)class Foo: """ Sample Foo class """ def foo(self): """ Sample foo method Returns: 2 """ return 2
def add(a, b): """ Adds the two arguments. >>> add(1, 3) 4 >>> add(1, '') Traceback (most recent call last): ... TypeError: unsupported operand type(s) for +: 'int' and 'str' """ return a + b if __name__ == '__main__': import doctest doctest.testmod()
Test-Driven Development is not about testing.
— Dan North
лишън!