Читаем Экстремальное программирование: Разработка через тестирование полностью

TestSuite

class TestSuite:

def __init__(self):

self.tests = []

def add(self, test):

self.tests.append(test)

(В языке Python оператор [] создает пустую коллекцию.)

Однако с реализацией метода run() возникают проблемы. Мы хотим, чтобы результаты срабатывания всех тестов накапливались в едином объекте класса TestResult. Таким образом, мы можем написать следующий код:

TestSuite

def run(self):

result = TestResult()

for test in tests:

test.run(result)

return result

Здесь оператор цикла «for test in tests» выполняет итерации по всем элементам последовательности tests, присваивает их по одному переменной цикла test и запускает соответствующий тест. Однако шаблон «Компоновщик» (Composite) подразумевает, что набор объектов должен обладать точно таким же интерфейсом, каким обладает отдельный объект. Если мы передаем параметр методу TestCase.run(), значит, мы должны передавать точно такой же параметр методу TestSuite.run(). Можно использовать одну из трех альтернатив.

• Воспользоваться встроенным в язык Python механизмом параметров со значениями по умолчанию. К сожалению, значение параметра по умолчанию вычисляется во время компиляции, но не во время выполнения, а мы не хотим повторно использовать один и тот же объект TestResult.

• Разделить метод на две части – одна создает объект TestResult, а вторая выполняет тест, используя переданный ей объект TestResult. Я не могу придумать хороших имен для двух частей метода, а это означает, что данная стратегия не является самой лучшей.

• Создавать объекты TestResult в вызывающем коде.

Мы будем создавать объекты TestResult в вызывающем коде. Этот шаблон называется «Накапливающий параметр» (Collecting Parameter).

TestCaseTest

def testSuite(self):

suite = TestSuite()

suite.add(WasRun("testMethod"))

suite.add(WasRun("testBrokenMethod"))

result = TestResult()

suite.run(result)

assert("2 run, 1 failed" == result.summary())

При использовании данного подхода метод run() не возвращает никакого явного значения:

TestSuite

def run(self, result):

for test in tests:

test.run(result)

TestCase

def run(self, result):

result.testStarted()

self.setUp()

try:

method = getattr(self, self.name)

method()

except:

result.testFailed()

self.tearDown()

Теперь мы можем облагородить обращение к тестовым методам в конце файла:

suite = TestSuite()

suite.add(TestCaseTest("testTemplateMethod"))

suite.add(TestCaseTest("testResult"))

suite.add(TestCaseTest("testFailedResultFormatting"))

suite.add(TestCaseTest("testFailedResult"))

suite.add(TestCaseTest("testSuite"))

result = TestResult()

suite.run(result)

print(result.summary())

Вызов тестового метода

Вызов метода setUp перед обращением к методу

Вызов метода tearDown после обращения к методу

Метод tearDown должен вызываться даже в случае неудачи теста

Выполнение нескольких тестов

Отчет о результатах

Строка журнала в классе WasRun

Отчет о неудачных тестах

Перехват и отчет об ошибках setUp

Создать объект TestSuite автоматически на основе класса TestCase

Здесь слишком много повторяющегося кода, от которого можно избавиться, если обеспечить способ конструирования набора тестов автоматически, исходя из предоставленного класса TestCase.

Однако вначале восстановим корректное выполнение четырех не неудачных тестов (эти тесты используют старый интерфейс функции run() без аргументов):

TestCaseTest

def testTemplateMethod(self):

test = WasRun("testMethod")

result = TestResult()

test.run(result)

assert("setUp testMethod tearDown " == test.log)

def testResult(self):

test = WasRun("testMethod")

result = TestResult()

test.run(result)

assert("1 run, 0 failed" == result.summary())

def testFailedResult(self):

test = WasRun("testBrokenMethod")

result = TestResult()

test.run(result)

assert("1 run, 1 failed" == result.summary())

def testFailedResultFormatting(self):

result = TestResult()

result.testStarted()

result.testFailed()

assert("1 run, 1 failed" == result.summary())

Обратите внимание, что каждый из тестов создает экземпляр класса TestResult – эту операцию можно выполнить однократно внутри метода setUp(). Благодаря реализации этой идеи мы упростим тесты, однако сделаем их несколько более сложными в прочтении:

TestCaseTest

def setUp(self):

self.result = TestResult()

def testTemplateMethod(self):

test = WasRun("testMethod")

test.run(self.result)

assert("setUp testMethod tearDown " == test.log)

def testResult(self):

test = WasRun("testMethod")

test.run(self.result)

assert("1 run, 0 failed" == self.result.summary())

def testFailedResult(self):

test = WasRun("testBrokenMethod")

test.run(self.result)

assert("1 run, 1 failed" == self.result.summary())

def testFailedResultFormatting(self):

self.result.testStarted()

self.result.testFailed()

assert("1 run, 1 failed" == self.result.summary())

def testSuite(self):

suite = TestSuite()

suite.add(WasRun("testMethod"))

Перейти на страницу:

Все книги серии Библиотека программиста

Программист-фанатик
Программист-фанатик

В этой книге вы не найдете описания конкретных технологий, алгоритмов и языков программирования — ценность ее не в этом. Она представляет собой сборник практических советов и рекомендаций, касающихся ситуаций, с которыми порой сталкивается любой разработчик: отсутствие мотивации, выбор приоритетов, психология программирования, отношения с руководством и коллегами и многие другие. Подобные знания обычно приходят лишь в результате многолетнего опыта реальной работы. По большому счету перед вами — ярко и увлекательно написанное руководство, которое поможет быстро сделать карьеру в индустрии разработки ПО любому, кто поставил себе такую цель. Конечно, опытные программисты могут найти некоторые идеи автора достаточно очевидными, но и для таких найдутся темы, которые позволят пересмотреть устоявшиеся взгляды и выйти на новый уровень мастерства. Для тех же, кто только в самом начале своего пути как разработчика, чтение данной книги, несомненно, откроет широчайшие перспективы. Издательство выражает благодарность Шувалову А. В. и Курышеву А. И. за помощь в работе над книгой.

Чед Фаулер

Программирование, программы, базы данных / Программирование / Книги по IT

Похожие книги