Ритм красный – зеленый – рефакторинг чрезвычайно важен для достижения успеха. Не бойтесь потратить дополнительные усилия, чтобы поддерживать этот ритм, – дополнительные усилия с лихвой окупят себя. Я достаточно часто попадаю в подобную ситуацию: сначала записываю тест, а потом оказывается, что для его реализации требуется выполнить не одно, а несколько изменений. Я неожиданно оказываюсь на большом расстоянии от зеленой полосы. Даже десять минут с красной полосой заставляют меня нервничать.
Когда тест оказывается слишком большим, я, прежде всего, пытаюсь усвоить урок. Почему тест оказался слишком большим? Что надо было сделать иначе, чтобы тест получился меньше по размеру?
Покончив с размышлениями, я удаляю изначальный тест и начинаю заново. «Похоже, заставить все эти три вещи работать одновременно – это слишком сложная задача. Однако если я вначале добьюсь успешной работы A, B и C, мне не составит труда заставить работать всю эту штуку целиком.» Иногда я действительно удаляю тест, однако в некоторых случаях я просто изменяю его имя так, чтобы оно начиналось на x, – в этом случае тестовый метод не будет выполнен. (Скажу вам по секрету, что иногда я вообще не трогаю изначальный тест. Да, да! Только т-с-с-с! Никому об этом не рассказывайте! Слава богу, в большинстве подобных случаев мне удается быстро заставить работать дочерний тест. Однако получается, что я в течение пары минут живу вместе с двумя сломанными тестами. Возможно, когда я так поступаю, я совершаю ошибку. Этот пережиток сохранился у меня с тех времен, когда я выполнял тестирование после завершения разработки или вообще не тестировал свой код.)
Попробуйте оба варианта. Прислушайтесь к своим ощущениям. Если у вас есть два сломанных теста, вы, как правило, начинаете программировать иначе. Делайте выводы.
Поддельный объект (Mock Object)
Как выполнять тестирование объекта, который базируется на сложном и тяжеловесном ресурсе? Создайте поддельную версию ресурса, которая будет возвращать константы.
Использование поддельных объектов – это тема для отдельной книги. Существует огромное количество материала, посвященного поддельным объектам [16]. Здесь я попытаюсь очень коротко познакомить читателей с этой концепцией.
Классическим примером является база данных. Чтобы запустить базу данных, требуется значительное время, поддержка чистоты базы данных требует дополнительных затрат, кроме того, если база данных располагается на удаленном сервере, ваши тесты будут связаны с конкретным физическим местоположением в сети. Наконец, база данных является емким источником ошибок разработки.
Чтобы уменьшить количество проблем, рекомендуется в процессе тестирования отказаться от работы непосредственно с базой данных. Большинство тестов пишется в отношении объекта, который функционирует подобно базе данных, однако располагается в оперативной памяти.
public void testOrderLookup() {
Database db = new MockDatabase();
db.expectQuery("select order_no from Order where cust_no is 123");
db.returnResult(new String[] {"Order 2","Order 3"});
.
}
Если объект MockDatabase не принимает ожидаемого запроса, он генерирует исключение. Если запрос корректен, объект возвращает нечто, напоминающее результирующий набор данных, состоящий из нескольких постоянных строк.
Помимо высокой производительности и надежности поддельные объекты обладают еще одним преимуществом: читабельностью. Если вы работаете с реальной базой данных, заполненной реальными данными, в результате обработки запроса вы можете получить ответ, состоящий из 14 строк. Возможно, вам будет нелегко понять, откуда взялось число 14 и в чем, собственно, состоит смысл теста.
Если вы хотите воспользоваться поддельными объектами, не следует хранить тяжеловесные ресурсы в глобальных переменных (даже если они замаскированы с использованием шаблона «Одиночка» (Singleton)). Если вы так поступите, вам придется вначале настроить глобальный поддельный объект, затем выполнить тест, а затем позаботиться о том, чтобы вернуть поддельный объект в исходное состояние.
В свое время я очень строго следил за выполнением этого правила. Мы вместе с Массимо Арнольди (Massimo Arnoldi) разрабатывали код, который взаимодействовал с набором курсов обмена валют, хранящимся в глобальной переменной. Для разных тестов требовалось использовать разные наборы данных, и в некоторых случаях курсы обмена валют должны были быть разными для разных тестов. Вначале мы пытались использовать для тестирования глобальную переменную, однако в конце концов нас это утомило, и однажды утром (смелые решения, как правило, приходят ко мне по утрам) мы решили передавать объект Exchange (в котором хранились курсы обмена) в качестве параметра везде, где это было необходимо. Мы думали, что нам придется модифицировать сотни методов. Однако дело кончилось тем, что мы добавили дополнительный параметр в десять или пятнадцать методов и по ходу дела подчистили другие аспекты дизайна.
Читать дальше
Конец ознакомительного отрывка
Купить книгу