21. Учет и контроль
Вызов тестового метода
Вызов метода setUp перед обращением к методу
Вызов метода tearDown после обращения к методу
Метод tearDown должен вызываться даже в случае неудачи теста
Выполнение нескольких тестов
Отчет о результатах
Строка журнала в классе WasRun
Метод tearDown() должен выполняться, даже когда в процессе выполнения теста возникло исключение. Однако чтобы добиться этого, мы должны перехватывать исключения. (Честно говоря, я уже пытался реализовать это, но у меня не получилось, и я вернул все на свои места.) Если при реализации перехвата исключений мы допустим ошибку, мы можем не заметить этого, так как стандартный механизм доклада об исключениях не будет функционировать.
Работая в стиле TDD, важно понимать, что особое значение имеет порядок, в котором вы реализуете тесты. Выбирая тест, над которым я буду работать дальше, я стараюсь выбрать тот, который, во-первых, послужит для меня источником новых знаний, а во-вторых, достаточно прост, чтобы я был уверен в том, что могу заставить его работать. Если я добиваюсь успешного выполнения этого теста, но захожу в тупик при реализации следующего, я вполне могу выполнить откат назад на два шага. Было бы неплохо, если бы среда разработки оказывала мне в этом помощь. Например, было бы неплохо, если бы в момент срабатывания всех тестов автоматически создавалась резервная копия всего исходного кода, с которым я работаю.
После выполнения всех тестов желательно получить информацию о том, как они выполнились, например: «запущено 5, неудачных 2: TestCaseTest.testFooBar – ZeroDivideException, MoneyTest.testNegation – AssertionError». Если тесты перестают выполняться или результаты перестают отображаться на экране мы, по крайней мере, сможем обнаружить ошибку. Однако наша инфраструктура не обязана знать обо всех разработанных тестах.
Пусть метод TestCase.run() возвращает объект класса TestResult с результатами выполнения теста (вначале тест будет только один, однако позже мы усовершенствуем этот объект).
TestCaseTest
def testResult(self):
test = WasRun("testMethod")
result = test.run()
assert("1 run, 0 failed" == result.summary())
Начнем с поддельной реализации:
TestResult
class TestResult:
def summary(self):
return "1 run, 0 failed"
Теперь сделаем так, чтобы в результате выполнения метода TestCase.run() возвращался объект класса TestResult:
TestCase
def run(self):
self.setUp()
method = getattr(self, self.name)
method()
self.tearDown()
return TestResult()
Теперь, когда все тесты выполнились успешно, можно сделать реализацию метода summary() реальной. Как и раньше, будем двигаться маленькими шажками. Для начала заменим количество выполненных тестов константой:
TestResult
def __init__(self):
self.runCount = 1
def summary(self):
return "%d run, 0 failed" % self.runCount
(Оператор % в языке Python является аналогом функции sprintf в языке C.) Однако runCount не может быть константой, это должна быть переменная, значение которой вычисляется исходя из количества выполненных тестов. Мы можем инициализировать эту переменную значением 0, а затем увеличивать ее на единицу при выполнении очередного теста.
TestResult
def __init__(self):
self.runCount = 0
def testStarted(self):
self.runCount = self.runCount + 1
def summary(self):
return "%d run, 0 failed" % self.runCount
Теперь мы должны позаботиться о вызове этого нового метода:
TestCase
def run(self):
result = TestResult()
result.testStarted()
self.setUp()
method = getattr(self, self.name)
method()
self.tearDown()
return result
Мы точно так же могли бы преобразовать константу «0», обозначающую количество тестов, потерпевших неудачу, в переменную, как сделали это с переменной runCount, однако существующие тесты этого не требуют. Поэтому напишем новый тест:
TestCaseTest
def testFailedResult(self):
test = WasRun("testBrokenMethod")
result = test.run()
assert("1 run, 1 failed", result.summary)
Здесь:
WasRun
def testBrokenMethod(self):
raise Exception
Вызов тестового метода
Вызов метода setUp перед обращением к методу
Вызов метода tearDown после обращения к методу
Метод tearDown должен вызываться даже в случае неудачи теста
Выполнение нескольких тестов
Отчет о результатах
Строка журнала в классе WasRun
Отчет о неудачных тестах
Мы немедленно замечаем, что исключение, генерируемое в методе WasRun.testBrokenMethod(), не перехватывается. Нам хотелось бы перехватить это исключение и в отчете о результатах тестирования отметить, что тест потерпел неудачу. Добавим соответствующий пункт в список задач.
Подведем итог. Мы
• разработали поддельную реализацию и начали поэтапно делать ее реальной путем замены констант переменными;
• написали еще один тест;
• когда тест потерпел неудачу, написали еще один тест меньшего масштаба, чтобы обеспечить выполнение неудачного теста.