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

<p>19. Сервируем стол (метод setUp)</p>

Начав писать тесты, вы обнаружите, что действуете в рамках некоторой общей последовательности (Билл Уэйк (Bill Wake) придумал сокращение 3A – Arrange, Act, Assert):

• вначале вы создаете некоторые тестовые объекты – Arrange;

• затем заставляете эти объекты действовать – Act;

• потом проверяете результаты их работы – Assert.

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

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

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

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

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

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

Первый этап – Arrange – зачастую совпадает для нескольких разных тестов, в то время как второй и третий этапы для разных тестов различаются. У меня есть два числа: 7 и 9. Если я сложу их, я должен получить 16; если я вычту второе из первого, я ожидаю получить –2; наконец, если я перемножу их, я полагаю, должно получиться 63. Операции и ожидаемые результаты различаются, однако исходные данные одни и те же – два числа: 7 и 9.

Если подобное повторяется раз за разом в разных масштабах (а ведь так оно и есть), мы должны подумать о том, как можно оптимизировать создание тестовых объектов. Возникает конфликт между следующими двумя ограничениями:

 Производительность. Мы хотим, чтобы тесты срабатывали как можно быстрее. Отсюда следует, что если одни и те же объекты используются в нескольких тестах, желательно, чтобы создание этих объектов выполнялось всего один раз.

 Изоляция. Успех или неудача одного теста никак не должны влиять на работу других тестов. Если несколько тестов используют одни и те же объекты и если один из тестов меняет внутреннее состояние какого-либо объекта, результаты работы остальных тестов, скорее всего, изменятся.

Взаимозависимость между тестами приводит к одному весьма неприятному эффекту: если один тест перестает работать, остальные десять тестов тоже перестают работать, несмотря на то, что тестируемый ими код выполняется правильно. Характерен также и другой, еще более неприятный эффект, когда порядок выполнения тестов имеет значение: если я запускаю тест A перед тестом Б, оба теста выполняются успешно, если я запускаю тест Б перед тестом А, тест А не выполняется. Или, еще хуже, код, проверяемый тестом Б, действует неправильно, однако из-за того, что тест А запускается перед тестом Б, тест Б выполняется успешно.

Итак, мы хотим избежать взаимозависимости между тестами. Предположим, что мы можем сделать процедуру создания объектов достаточно быстрой. В этом случае мы могли бы создавать объекты для теста каждый раз перед выполнением очередного теста. Этот подход в замаскированном виде уже использовался нами в классе WasRun, в котором требовалось, чтобы перед запуском теста флаг wasRun сбрасывался в состояние «ложь». Напишем тест:

TestCaseTest

def testSetUp(self):

test= WasRun("testMethod")

test.run()

assert(test.wasSetUp)

Чтобы запустить этот код, необходимо добавить в конец нашего файла строку TestCaseTest(«testSetUp»). run(). Интерпретатор вежливо сообщает нам, что атрибут с именем wasSetUp отсутствует. И немудрено, ведь мы пока еще не определили значение этого атрибута. Вот необходимый для этого код:

WasRun

def setUp(self):

self.wasSetUp= 1

Однако метод setUp() должен быть откуда-то вызван. Обращение к методу setUp() – это работа класса TestCase. Добавим соответствующий код:

TestCase

def setUp(self):

pass

def run(self):

self.setUp()

method = getattr(self, self.name)

method()

Чтобы заставить тест работать, мы сделали целых два шага – это слишком много, особенно если учесть, что мы движемся почти вслепую. Проверим, работает ли тест? Да, работает. Однако если вы хотите чему-то научиться, попробуйте придумать, как мы можем заставить тест работать, изменяя лишь по одному методу за один шаг.

Немедленно воспользуемся новым механизмом, чтобы сократить длину наших тестов. Прежде всего упростим класс WasRun, для этого перенесем процедуру установки флага wasRun в метод setUp():

WasRun

def setUp(self):

self.wasRun = None

self.wasSetUp = 1

Теперь можно упростить метод testRunning() – освободить его от

обязанности проверять состояние флага перед вызовом тестового метода. Можем ли мы быть настолько уверенными в правильной работе нашего кода? Только при условии, что в наборе тестов присутствует тестовый метод testSetUp(). Это часто встречающийся шаблон – один тест может быть простым, только если в системе имеется другой тест, выполняющийся успешно:

TestCaseTest

def testRunning(self):

test = WasRun("testMethod")

test.run()

assert(test.wasRun)

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

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

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

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

Чед Фаулер

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

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