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

На архитектурном и детальном уровнях проектирования применяется та же идея. Рассматривая наш проект, мы представляем себе наши идеи как можно большим числом способов и проверяем, сможем ли мы их разрушить. Это важно, что мы используем некоторые черты проекта, такие как число возможных состояний на входе, чтобы показать, что система, которую мы проектируем, будет устойчива во всех случаях, показав, что мы рассмотрели все случаи. Это не означает, что мы пытаемся просмотреть все варианты -- вместо этого мы находим средства сгруппировать эти варианты, и показать, что мы рассмотрели все группы.

При пошаговой отладке с помощью символьного графического отладчика в каждой точке принятия решения нам следует рассмотреть все обстоятельства, при которых принят путь, который мы выбираем, и также следует проследить все другие варианты путей.

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

Мудрые архитекторы обычно разделяют свои проекты на уровни так, что прослеживаются отдельные дискретные стадии при переходе от кода, взаимодействующего с пользователем, к коду, взаимодействующему с операционной системой. Каждый из этих уровней предоставляет возможность написать небольшое тестовое приложение. Обычно этими возможностями следует воспользоваться, поскольку хотя это, как может показаться, и вызывает рост стоимости, выявление ошибок, которые не имеют хорошо определенных тестовых точек, может неимоверно осложнить фазу окончательного тестирования и отнимет кучу времени. Чтобы полностью воспользоваться этими тестовыми возможностями, нам следует предусмотреть тестирование при определении API для наших уровней. Можно ли упростить определения API так, что мы сможем уменьшить количество всех возможных вызовов, которые не имеют смысл? Каждый уровень обязан проверять свой вход, либо, если время очень критично, требовать предварительно выверенных входных значений. Тестирование должно гарантировать, что эта логика работает в соответствии условиями функционирования этого уровня. Если API можно упростить, то одновременно автоматически упрощаются требования к тестированию.

Соображения, которые применимы к уровням, также применимы к процессам времени исполнения. Большинство нетривиальных систем требуют нескольких процессов для взаимодействия как внутри отдельной платформы, так и через сеть. Функциональность этих процессов должна быть распределена таким образом, чтобы их можно было протестировать, в идеале изолированно -- из командной строки или с помощью скрипта.

Иногда нам не удается избежать внесения неоднородности (разрывности) решения, которая отсутствует в проблеме. Например, если наша база данных настолько велика, что мы должны распределить ее по нескольким машинам (и имеющаяся у нас коммерческая СУБД не обеспечивает такой возможности), то нам нужно распознать те точки, где должна измениться логика наших программ, чтобы работать на другой системе, и убедиться (тестированием), что это изменение проведено правильно.

У разработчиков объектных систем есть особенно простая стратегия автоматизации тестирования. Каждый класс (или ключевые классы, по решению архитектора) может иметь соответствующий класс, заданный так, что подменяет (эмулирует) методы системного класса. Это так хорошо работает потому, что описание этого класса стимулирует тестирование внешнего интерфейса класса в стандартизованном и хорошо определенным формате (в чем заключается суть объектов). Поэтому каждый класс может сопровождаться собственным тестовым кодом, который просто нужно заключить в небольшую обрамляющую программку для автоматизации тестирования. Эти тестовые классы иногда называют классами "янь" ("yang"), а поставляемые классы называют соответственно классами "инь" ("yin").