Выбрать главу

Сара Маунт

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

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

Чтобы компенсировать этот недостаток, Стивен Джонсон написал инструмент под названием lint — для прочесывания кода на предмет блох, — и реализовал в нем некоторые методы статического анализа, исключенные из компилятора C. Однако средства статического анализа прославились тем, что давали большое количество ложных срабатываний и часто предупреждали о нарушении стилистических правил, следовать которым необязательно.

Сегодняшний пейзаж языков, компиляторов и средств статического анализа весьма отличается от прежнего. Память и процессорное время стали относительно дешевыми, поэтому компиляторы могут позволить себе проверку большего числа ошибок. Почти для каждого языка существует хотя бы один инструмент, выявляющий нарушения стилистических правил, стандартные ошибки и иногда трудные для обнаружения ошибки типа возможного разыменования нулевого указателя. Более сложные инструменты, такие как Splint для C или Pylint для Python, допускают настройку, то есть возможность выбрать ошибки и предупреждения, о которых будет сообщать инструмент, с помощью файла конфигурации, параметров командной строки или настроек IDE. Splint даже позволяет аннотировать код комментариями, поясняющими работу программы.

Если эти средства не помогают, и вам приходится искать простые ошибки или нарушения стандартов, которые не обнаруживают ваш компилятор, IDE или инструмент типа lint, можно написать собственный статический анализатор. Это не так трудно, как может показаться. Большинство языков, особенно динамических, в составе стандартной библиотеки предлагают абстрактное синтаксическое дерево и инструменты компилирования. Вам не помешает заглянуть в дальние уголки стандартных библиотек, используемых разработчиками языка, с которым вы работаете, так как там встречаются сокровища, полезные для статического анализа и динамического тестирования. Так, в стандартной библиотеке Python есть дизассемблер, который сообщит вам, какой байт-код использовался для генерации некоторого скомпилированного кода или объекта. Многим покажется, будто это какой-то загадочный инструмент для команды разработчиков python-dev, разрабатывающей компилятор Python, но для повседневной работы он удивительно полезен. Например, с помощью этой библиотеки можно дизассемблировать последнюю трассировку стека и точно выяснить, какая команда байт-кода сгенерировала последнее необработанное исключение.

Так что не ограничивайте свой контроль качества одним тестированием — используйте инструменты для анализа и не бойтесь создавать свои собственные.

Тестируйте требуемое, а не случайное поведение

Кевлин Хенни

Типичное заблуждение при тестировании — воображать, что тестировать необходимо именно то, что делает реализация. На первый взгляд в этом не видно ничего дурного. Однако если сформулировать эту проблему иначе, она становится понятнее: частой ошибкой при тестировании является привязка тестов к особенностям реализации, тогда как эти особенности являются случайными и не имеют отношения к требуемой функциональности.

Когда тесты жестко связаны с особенностями реализации, изменения в реализации, которые в действительности совместимы с требуемым поведением, могут привести к отказу тестов. Из-за этого будут возникать ложные сообщения об ошибках. Обычно программисты реагируют на это исправлением теста или исправлением кода. Принятие ложного срабатывания за истинное часто есть следствие страха, неуверенности и сомнений. Это равносильно принятию случайного поведения за требуемое. При исправлении теста программист либо изменяет тест так, чтобы он проверял требуемое поведение (что хорошо), либо привязывает тест к новой реализации (что плохо). Тесты должны быть достаточно точными, но они должны быть и правильными.