представленные в виде дерева DOM или потока. Познакомились мы и с интерфейсом REXML к языку XPath.
Был продемонстрирован разбор информации из новостных каналов, представленных в формате на базе XML. Библиотека rss
умеет работать только с форматом RSS, а библиотека feedtools
понимает форматы RSS и Atom (и умеет преобразовывать из одного в другой).
Мы также видели, как можно читать и манипулировать графическими изображениями разного формата с помощью библиотеки RMagick. Рассмотрели мы и API рисования, позволяющий включать в изображение произвольный текст и геометрические фигуры. Наконец, мы показали, как с помощью библиотеки PDF::Writer
можно создавать из программы сложные PDF-документы высокого качества.
Следующая глава посвящена совсем другой теме. Речь пойдет об эффективном тестировании и отладке написанных на Ruby программ.
Глава 16. Тестирование и отладка
Неполадки в блоке АЕ-35. В ближайшие семьдесят два часа блок может отказать.
Тестирование — вещь важная. Все компетентные программисты об этом знают, хотя не всегда этот вопрос стоит для них на первом месте.
Конечно, исчерпывающее тестирование, как правило, невозможно. Программа сколько-нибудь заметного размера на протяжении своего жизненного цикла обязательно преподнесет сюрпризы. Максимум, что мы можем сделать, — тестировать тщательно и избирательно, стараясь проверить как можно больше.
Исторически сложилось так, что программисты не всегда тестируют как положено. Объясняют это обычно тем, что тесты трудно готовить и прогонять, что вся процедура требует ручного вмешательства или отнимает слишком много времени.
В 1990 году в сообществе программистов стала распространяться «культура тестирования». Идеи экстремального программирования и управляемой тестами разработки начали овладевать умами разработчиков по всему миру.
Являетесь ли вы твердокаменным приверженцем идеологии «тестируй с самого начала», не так существенно. Важно, что любой человек может воспользоваться инструментами, которые позволяют автоматизировать тестирование, упростив написание и прогон тестов.
Такие инструменты, как Test::Unit
и ZenTest, написать на Ruby было проще в силу динамичности и гибкости языка. Не менее легко и (посмею ли сказать?) приятно ими пользоваться. Внес изменение в программу, а потом смотришь, как все тесты успешно доходят до конца, — положительно в этом что-то есть!
Помимо этих инструментов в Ruby есть еще немало программ и библиотек для отладки, профилирования и испытания различных путей исполнения. Эта глава посвящена обзору имеющихся средств.
16.1. Библиотека Test::Unit
«Стандартный» способ автономного тестирования компонентов в Ruby — библиотека Test::Unit
Натаниэля Тэлбота (Nathaniel Talbott). Она была включена в дистрибутив Ruby еще в 2001 году.
В этой библиотеке для анализа тестового кода применяется отражение. Когда вы создаете подкласс класса Test::Unit::TestCase
, все методы, имена которых начинаются с test
, считаются тестовыми.
require 'test/unit'
class TC_MyTest < Test::Unit::TestCase
def test_001
# ...
end
def test_002
# ...
end
# ...
end
Методы необязательно нумеровать, как показано в этом примере. Это мое личное соглашение, но, конечно, есть и другие.
Нежелательно и, пожалуй, даже неправильно составлять тесты так, чтобы их поведение зависело от порядка запуска. Однако Test::Unit
прогоняет их в алфавитном (лексикографическом) порядке, поэтому, нумеруя свои методы, я вижу, как они выполняются в определенной последовательности.
Я также предпочитаю включать некий «заголовок» в имя метода (описывающий его область действия или назначение):
def test_053_default_to_current_directory
# ...
end
def test_054_use_specified_directory
# ...
end
Кроме прочего, неплохо оставлять хотя бы однострочный комментарий, касающийся цели и смысла теста. Вообще говоря, у каждого теста должна быть только одна цель.
А если нужно организовать некую среду выполнения, для чего требуется время? Неразумно делать это для каждого теста, и мы не вправе завести для данной цели отдельный метод (поскольку поведение не должно зависеть от порядка прогона).
Если всем тестам нужна особая среда, можно воспользоваться методами класса setup
и teardown
. Возможно, вам это покажется странным, но вызываются они для каждого теста. Если вы хотите выполнить настройку один раз, перед прогоном одного конкретного или всех тестов, то можете поместить соответствующий код в тело класса раньше всех тестовых методов (или даже до самого класса).
А если после выполнения всех тестов нужно разрушить созданную среду? По техническим причинам (так уж работает библиотека Test::Unit
) сделать это трудно. «Самый лучший» способ — переопределить метод run всего комплекта тестов (но не метод класса run
), обернув его функциональность. Рассмотрим пример в листинге 16.1.
require 'test/unit'
class MyTest < Test::Unit::TestCase