Для того чтобы протестировать полную систему, необходимо создать все ее составные части (модули). Это может занять значительное время, поэтому многие системные тесты выполняются только один раз в сутки (часто ночью, когда предполагается, что разработчики спят) после тестирования всех модулей по отдельности. В этом процессе ключевую роль играют регрессивные тесты. Самой подозрительной частью программы, в которой вероятнее всего кроются ошибки, является новый код и те области кода, в которых ранее уже обнаруживались ошибки. По этой причине важной частью тестирования (на основе регрессивных тестов) является выполнение набора предыдущих тестов; без этого крупная система никогда не станет устойчивой. Мы можем вносить новые ошибки с той же скоростью, с которой удаляются старые.
26.3.4.1. Зависимости
Зачем человеку сидеть перед экраном с графическим интерфейсом и “клевать”? Причина заключается в том, что тестировщики не могут предвидеть возможные действия пользователя, которые он может предпринять по ошибке, из-за неаккуратности, по наивности, злонамеренно или в спешке. Даже при самом лучшем и самом систематическом тестировании всегда существует необходимость, чтобы систему испытывали живые люди. Опыт показывает, что реальные пользователи любой значительной системы совершают действия, которые не предвидели даже опытные проектировщики, конструкторы и тестировщики. Как гласит программистская пословица: “Как только ты создашь систему, защищенную от дурака, природа создаст еще большего дурака”.
Итак, для тестирования было бы идеальным, если бы графический пользовательский интерфейс просто состоял из обращений к точно определенному интерфейсу главной программы. Иначе говоря, графический пользовательский интерфейс просто предоставляет возможности ввода-вывода, а любая важная обработка данных выполняется отдельно от ввода-вывода. Для этого можно создать другой (неграфический) интерфейс.
Это позволяет писать или генерировать сценарии для главной программы так, как мы это делали при тестировании отдельных модулей (см. раздел 26.3.2). Затем мы можем протестировать главную программу отдельно от графического пользовательского интерфейса.
Интересно, что это позволяет нам наполовину систематически тестировать графический пользовательский интерфейс: мы можем запускать сценарии, используя текстовый ввод-вывод, и наблюдать за его влиянием на графический пользовательский интерфейс (предполагая, что мы посылаем результаты работы главной программы и графическому пользовательскому интерфейсу, и системе текстового ввода-вывода). Мы можем поступить еще более радикально и обойти главное приложение, тестируя графический пользовательский интерфейс, посылая ему текстовые команды непосредственно с помощью небольшого транслятора команд.
Приведенный ниже рисунок иллюстрирует два важных аспекта хорошего тестирования.
• Части системы следует (по возможности) тестировать по отдельности. Только модули с четко определенным интерфейсом допускают тестирование по отдельности.
• Тесты (по возможности) должны быть воспроизводимыми. По существу, ни один тест, в котором задействованы люди, невозможно воспроизвести в точности.
Рассмотрим также пример проектирования с учетом тестирования, которое мы уже упоминали: некоторые программы намного легче тестировать, чем другие, и если бы мы с самого начала проекта думали о его тестировании, то могли бы создать более хорошо организованную и легче поддающуюся тестированию систему (см. раздел 26.2). Более хорошо организованную? Рассмотрим пример.