Модульное тестирование

Модульное тестирование

...в этих людях, Отмеченных хотя б одним изъяном,.
Все их достоинства — пусть нет им счета И пусть они, как совершенство, чисты, — По мненью прочих, этим недостатком Уже погублены. Шекспир. Гамлет.
В связи с потенциальным ущербом от каждой программной ошибки и возрастающей сложностью нахождения и исправления ошибок по ходу роста программы раннее и частое тестирование становится важной частью процесса разработки.
Сразу после реализации частей программы мы немедленно приступаем к тестированию (рис. 8.1).

Модульное тестирование

...в этих людях, Отмеченных хотя б одним изъяном,.
Все их достоинства — пусть нет им счета И пусть они, как совершенство, чисты, — По мненью прочих, этим недостатком Уже погублены. Шекспир. Гамлет.
В связи с потенциальным ущербом от каждой программной ошибки и возрастающей сложностью нахождения и исправления ошибок по ходу роста программы раннее и частое тестирование становится важной частью процесса разработки.
Сразу после реализации частей программы мы немедленно приступаем к тестированию (рис. 8.1).
♦ Разделы 8.1-8.6.
♦ Упражнения.
♦ Пример: Индивидуальная программная документация (PSD), часть 2 Вопросы, рассматриваемые в этой главе:.
♦ Понимание значения модульного тестирования.
♦ Различие между тестированием методами «черного» и «белого ящика».
♦ Разработка адекватной зоны охвата.
♦ Изучение стандартов тестирования.
4- Инспектирование плана модульного тестирования.
8.1. Введение в модульное тестирование 8.1.1. Цели тестирования.
Мы не можем протестировать программу абсолютно во всех аспектах, поскольку число вариантов работы нетривиальной компьютерной программы может быть неограниченным. Следовательно, тестирование не может доказать отсутствия ошибок в программе, в то время как доказательство корректности способно это сделать. Тестирование может только показать присутствие ошибок.
Рис. 8.1. План разработки программ: темы главы 8
Тестирование часто неправильно воспринимается как процесс подтверждения корректности кода, что можно выразить таким высказыванием: «Протестируй это, чтобы убедиться, что тут все правильно». Иногда, однако, это является целью тестирования, особенно незадолго до отсылки продукта или при регрессионном тестировании (объясняется в следующей главе). Главная цель тестирования далека от подтверждения корректности. Его цель не в том, чтобы показать удовлетворительную работу программы, а в том, чтобы четко определить, в чем работа программы неудовлетворительна!.
Время, использованное на тестирование, требует значительных затрат, и мы стараемся получить от этих затрат максимальную прибыль. Для данной тестируемой программы, чем больше дефектов будет найдено на каждый доллар зарплаты, тем выше выигрыш от вложений в тестирование. Следовательно, целью тестирования является обнаружение как можно большего числа дефектов с высоким уровнем важности. Резюмируя сказанное выше, перечислим «золотые правила» тестирования.
♦ Цель тестирования: максимизировать число и важность обнаруженных дефектов на каждый затраченный доллар.
♦ Поэтому: начинайте тестирование рано.
♦ Ограниченные возможности тестирования: тестирование может установить только присутствие дефектов, и никогда — их отсутствие.
♦ Для установления факта отсутствия дефектов используйте доказательства корректности.
Тестирование оценивается более чем половиной времени, затраченного на проект. Наградой за нахождение дефекта на ранней стадии процесса является по крайней мере десятикратная экономия по сравнению с обнаружением этого же дефекта на этапе интеграции или, еще хуже, после отправки заказчику. Следовательно, мы должны тестировать рано и часто.
Что касается идеальной гарантии качества в общем, тестирование кода должны проводить люди, не участвовавшие в его разработке. Когда инженер разрабатывает код, он создает для себя представление того, что код должен выполнять. Поэтому в то же время он разрабатывает типичную среду, в которой этот код должен выполняться. Можно смело считать, что код дает немного ошибок в этой конкретной среде. Следовательно, эта среда является основой тестов разработчика. Именно поэтому, когда человек тестирует свой собственный код, он часто прячет каждый дефект, который необходимо найти.
Модульное тестирование является ранним типом тестирования. Следующий уровень состоит из интегрального тестирования. Здесь валидируется общая функциональность каждой стадии конкретной программы. Наконец, система и различные приемосдаточные тесты валидируют финальный продукт, как описано в следующей главе. Уже разработанные варианты использования также берутся в качестве основы для некоторых из этих тестов. Типы тестирования и связь между ними проиллюстрированы на рис. 8.2.
Рис. 8.2. Тестирование: общая картина
В этой главе рассказывается о модульном тестировании; все другие варианты тестирования описаны в главе 9.
8.1.2. Значение модульного тестирования.
Цель модульного тестирования — проверить структуру, в то время как цель всех других видов тестирования обычно заключается в проверке функциональности. В качестве аналогии представьте себе тестирование каждой опоры моста на заводе. Это является неким подобием модульного тестирования, поскольку в этом случае тест затрагивает элементы структуры. Тест, состоящий из проезда автомобиля по частично сконструированному мосту, напротив, не будет модульным тестированием. Функции обычно являются наименьшими частями программы, к которым может быть применено модульное тестирование (см. рис. 8.2). Следующим по величине элементом является модуль (класс в случае объектно-ориентированной ориентации). Иногда комбинации модулей рассматриваются в целях тестирования как модули.
Модули, к которым применяется модульное тестирование, являются блоками при построении программы, а не отдельными кирпичами, на которых строится дом. Хотя на дом не сильно повлияют несколько бракованных кирпичей, программное приложение может оказаться очень чувствительным к дефектам в отдельных блоках конструкции. Если дефектные части будут встроены в программы, может понадобиться огромное количество времени на их нахождение и исправление. Поэтому блоки программы должны быть абсолютно надежными, что и является целью модульного тестирования.
Модульное тестирование является дополнением к инспектированию и использованию формальных методов проверки корректности.
В терминологии USDP модульное тестирование проводится преимущественно на итерациях проектирования, а также на ранних этапах итерации конструирования [64] (рис. 8.3).
Рис. 8.3. Модульное тестирование в USDP
8.1.3. Типичный план модульного тестирования.
Типичный план модульного тестирования, основанный на стандарте IEEE 10081987, показан на рис. 8.4. Далее объясняются шаги процесса модульного тестирования.
1. Входными данными для процесса планирования теста являются требования и детальный проект. Вспомните, что каждое требование соответствует хорошо определенному коду (функции, где возможно). Детальный проект обычно состоит из дополнительных классов и методов. Они также сказываются на качестве программы и должны быть протестированы в том же объеме, что и отдельные требования. Выходными данными процесса планирования теста является модульный план тестирования (например, «(1) тест метода 84; (2) тест метода 14; ...; (т) тест класса 26, ...»).
2.
Следующим шагом является получение входных и выходных данных, ассоциирующихся с каждым тестом. Некоторые из этих данных могут быть уже получены из предыдущих тестов (например, предыдущие итерации, предыдущие версии продукта или предыдущие выпуски). Результатом на этом шаге является набор тестов.
3.
Наконец, тесты исполняются.
Рис. 8.4. План модульного тестирования
Далее приведены этапы, требуемые стандартом IEEE для модульного тестирования (1008-1987). Они расширяют только что описанный план модульного тестирования.
1.
Спланировать общий подход, ресурсы и расписание.
2.
Определить характеристики, которые следует протестировать, исходя из требований.
3.
Обновить общий план.
4.
Разработать набор тестов.
5.
Реализовать обновленный план и проект.
6.
Выполнить тестовые процедуры.
7.
Проверить окончание работы.
8.
Оценить тестирование и модули.
8.2. Типы тестов.
Хотя Гленфорд Майерс написал свою книгу «The Art of Software Testing» [82] еще в 1978 году, эта небольшая книга остается классикой, и она сыграла свою роль в написании этой главы.
8.2.1. Тестирование «черного ящика», «белого ящика» и «серого ящика».
В этом разделе будет дано определение тестирования «черного», «белого» и «серого ящика». В остальной части главы будет описано, как планировать, проектировать и выполнять такие тесты.
Когда мы интересуемся исключительно тем, как программа или ее часть предоставляет соответствующие выходные данные, мы тестируем ее на каждое требование, используя подходящие входные данные. Это называется тестированием «черного ящика», поскольку мы не обращаем внимания на то, что находится внутри «ящика» (программы): «ящик» может быть «черным». Тесты «черного ящика» могут быть эффективны, если мы можем убедиться, что они исчерпывают все комбинации входных данных. Это докажет заказчику, что все требования удовлетворены. Однако никакое тестирование не охватывает всех возможностей.
Тестирование «черного ящика» похоже на тестирование моста путем проезда по нему нескольких комбинаций различных транспортных средств. Это неэффективно, поскольку нам нужно проверить и составные части моста, и то, как они объединены в систему. Последнее называется идеей «тестирования белого ящика». Тестирование «черного ящика» и «белого ящика» проиллюстрировано на рис. 8.5.
Рис. 8.5. Тестирование «черного ящика», «серого ящика» и «белого ящика»
Целью тестирования «белого ящика» является тестирование наиболее ненадежных путей программы. Для выполнения тестирования «белого ящика» мы сначала разбиваем проект программы на отдельные элементы и ищем пути и другие разбиения для управления и данных. Затем мы проектируем тесты, прослеживающие все или некоторые из этих путей, и проверяем все составные части. Более наглядным названием этих действий было бы «тестирование стеклянного ящика».
Тестирование «серого ящика» рассматривает внутреннюю работу программы или модуля, но только до некоторой степени. Сюда могут быть также отнесены и некоторые аспекты тестирования «черного ящика».
8.2.2. Разбиение равнозначности для тестирования «черного ящика».
Поскольку у нас нет возможности протестировать все комбинации входных данных, мы ищем представительные варианты тестов. Набор возможных вариантов тестов для трех переменных в финансовой программе — капитал, процентная ставка и оценка инфляции — изображен на рис. 8.6. Проблема заключается в нахождении наилучшего представления бесконечного множества возможностей наиболее представительным определенным множеством. Разбиение равнозначности — это разбиение входных тестовых данных на подмножества таким образом, чтобы при условии успешного выполнения одного из них остальные элементы подмножества также с большой вероятностью прошли бы тест успешно. Например, при тестировании функции setNameC String ) в классе GameCharacter успешное завершение тестового вызова setName( «Наггу» ) означает, что у нас, вероятно, не будет проблем при тестировании функции setNameO с любой строкой из пяти символов. Более того, мы, вероятно, можем расширить это разбиение равнозначности на «все имена не менее чем с одним и не более чем с maxNumCharsInNameC) символами».
Рис. 8.6. Диапазон входных данных для тестирования
Разбиение равнозначности для метода, вычисляющего величину в зависимости от заданного капитала, процентной ставки и оценки инфляции, представлено на рис. 8.7. Например, область, закрашенная на рис. 8.7 серым, соответствует следующему разбиению равнозначности:.
«оценка инфляции между 15 и 20 %», «капитал от $65 до $100» и «процентная ставка между 0 и 5 %».
Максимальная прибыль от тестирования обычно достигается при анализе граничных значений, о котором речь пойдет далее.
Рис. 8.7. Разбиение равнозачности входных данных тестирования
8.2.3. Анализ граничных значений для тестирования «черного ящика».
К разбиениям равнозначности обычно прибегают при исследовании граничных значений внутренних переменных программы. Например, оценка инфляции должна лежать между 1 и 20 %, что дает две границы. Предположим, что значения инфляции до 15 % и значения, превышающие эту величину, программа обрабатывает по-разному. Это дает нам еще одну дополнительную границу (рис. 8.7).
В функции setName( String ), упоминавшейся ранее, границами считаются строки нулевой длины и строки длиннее maxNumCharsInNameO. Границы могут быть также представлены в форме уравнения, например х + у = 7. Оно может быть выражено условием типа whi1e(x+y.
В тестировании проекта значения, лежащие за пределами этих границ (например, недействительный ввод данных), также используются в качестве тестовых данных. Тестовые данные генерируются после того, как будут установлены границы эквивалентных классов (рис. 8.8).
Рис. 8.8. Диапазон тестирования: элементарные случаи
8.2.4. Рассмотрение утверждений для тестирования «белого ящика».
Каждое утверждение в программе должно быть выполнено по крайней мере одним тестом. Анализ каждого утверждения обязателен. В качестве примера (рис. 8.9) покажем, что выполнение всех утверждений не гарантирует корректности программы (см. [82]). Требования для программы на рис. 8.9 определены блок-схемой. Тестовый вариант и—2, v=0, х=3 выполняет каждую команду реализации и генерирует корректные выходные данные (х=2,5). Однако программа дефектна, так как в ней не реализована блок-схема. Например, в случае u=3, v=0. х=3 программа генерирует ответ х=1, хотя должно быть х=2, как показано на блок-схеме.
У.
Рис. 8.9. Область охвата теста «Каждое утверждение не является достаточным»
8.2.5. Рассмотрение решений для тестирования «белого ящика».
Обзор решений гарантирует, что программа принимает каждую ветвь каждого решения. Например, рассмотрим блок-схему на рис. 8.10. Убедимся, что каждое да и нет можно получить в результате хотя бы одного теста из набора тестов.
Циклы эффективно реализуют последовательность условных операторов. Например, цикл.
for( 1=0; I
< 3; ++i).
v[i] = w[I+l] + w[i];.
можно «раскрыть» в такую последовательность условных операторов:.
// For i==0: i = 0;.
v[i] = w[i+l] + w[i ];.
++i; // For i—1: if (i
< 3){.
v[i] = w[i+l] + w[i]: ++i;.
}.
// For i==2: if (i
< 3){.
v[i] = w[i+l] + w[i ]; ++i;.
}
Рис. 8.10. Направления, которые необходимо проверить
Обзор решений гарантирует выполнение каждой итерации каждого цикла. Его можно реализовать путем определения каждого цикла и подбора входных данных таким образом, чтобы каждая итерация каждого цикла выполнилась хотя бы один раз. Этот подход не так сложен для циклов for, однако он представляет некоторые сложности для циклов while. Например, как бы мы стали тестировать итерацию следующего цикла:.
whlletu
< v) {...} // ?.
Иногда все возможные варианты можно просчитать, иногда их можно разбить на типовые группы. Однако в некоторых случаях полное рассмотрение решений с помощью циклов while практически невозможно. Вспомните, что циклы while часто допускают применение формальных методов и инспектирования. Это иллюстрирует вспомогательную природу формальных методов, проверок и тестирования.
Рассмотрение решений обычно содержит в себе рассмотрение утверждений, поскольку все последующие точки ветвления во всех комбинациях обычно включают в себя каждое утверждение кода. Рассмотрение решений может оказаться недостаточным из-за того, что в некоторых решениях могут скрываться другие. Например, в утверждении.
if( А && В )...
условие В никогда не тестируется, если условие А ложно. Аналогично, в утверждении 1f( А 11 В )...
условие В никогда не тестируется, если условие А истинно.
Для решения таких проблем можно использовать тестирование с множественными условиями. Это тестирование представляет собой полный объем условного тестирования, проверяющий каждую комбинацию каждого условия по крайней мере один раз. Разработка таких тестовых вариантов может оказаться довольно скучным занятием, поскольку необходимо проследить в программе каждое условие, чтобы определить подходящие входные данные. Важную роль в создании этих тестовых комбинаций играет программное обеспечение, генерирующее тесты автоматически.
До сих пор мы уделяли основное внимание проверке того, что все условия выполняются и что результаты получаются ожидаемыми. Эта идея лежит в основе техники «серого ящика», согласно которой мы тестируем входные и выходные данные («черный ящик») наряду со всеми утверждениями («белый ящик»). Нам также нужно проверить, что по ходу работы программа проходит через все предполагаемые состояния. Эту задачу решает тестирование инвариантов.
8.2.6. Тестирование на основе инвариантов.
Вспомните, что инварианты — это утверждения, связывающие переменные. Они выражают состояния. Например, для выражения состояния банкомата после того, как клиент вставил карту и ввел корректный личный код, используется нижеследующий типичный инвариант:.
( cardlnserted==true ) && ( pin == VALID ).
Как уже отмечалось в главе 7, мы используем инварианты для поддержки интеллектуального управления вычислениями и для доказательства корректности кода. Во многих случаях эти утверждения остаются неизменными в стратегически важных блоках кода. Часто бывает полезно добавлять в исходный код команды, сообщающие о том, действительно ли инвариант, который мы предполагаем истинным, сохраняется таковым во время работы программы. Эта техника «белого ящика» называется тестированием, основанным на инвариантах. Например, если сумма переменных х и у во время вычисления должна быть равной 10, во время работы программы можно проверять инвариант assert(x + у = 10);.
Функция assert() может сообщить об истинности или ложности своего аргумента. Желательно, чтобы вычисления приостанавливались в том случае, когда инвариант ложен, и т. д. Иногда функции assert О оставляют в исходном коде и для работы программы, например:.
assert( potentialTyFatalXRayDose.
хотя обычно инварианты используются только для проверки корректности программы.
Предположим, что мы хотим использовать тестирование инвариантов для программы тах() из раздела 7.4 о доказуемой корректности программ. Инварианты, которые должны быть удовлетворены, можно выразить способом, показанным в листинге 8.1.
Листинг 8.1. Тестирование на основе инвариантов.
public static boolean checkAssertion.
( int loopSoFarP, int indexOfMaxP, doublet] arrayP ).
{.
// Определение блока проверки.
// Сначала установите следующие булевы переменные:.
boolean ЫМ = true; /* Означает, что значения аггауРП меньше.
arrayP[indexOfMaxP] для всех индексов */ if( indexOfMaxP != О ).
for ( int u = 0; u
< indexOfMaxP; ++u ).
ЫМ &= ( arrayP[u]
< arrayP[indexOfMaxP] ); boolean b2M = true: /*0значает, что значения arrayP[] не более.
arrayP[indexOfMaxP] для индексов indexOfMaxP ... loopSoFarP*/ for( int v = indexOfMaxP; vЬ2М &= С arrayP[v].
if.
( // Цикл дошел до индекса loopSoFarP.
( 0&& //IndexOfMaxP - это индекс( 0&& ЫМ && Ь2М // ...где встретили первый максимум.
{ System.out.println( "Утверждение верно" );.
return true:.
else.
{ System.out.println( "Утверждение неверно" ):.
retuen false;.
}.
Теперь можно применять тестирование инвариантов каждый раз, когда предполагается, что инвариант будет истинным в программе (листинг 8.2). Пример с программой тах() является очень простым, но количество проблем, связанных с тестированием инвариантов, несоизмеримо с размером тестируемого кода. Однако для более сложного кода, подлежащего тестированию, время настройки может иногда оказаться разумным, а выгода (количество ошибок, найденных за каждый затраченный час) гораздо выше.
Листинг 8.2. Применение тестирования на основе инвариантов к тах().
/** Находит индекс и значение первого из наибольших элементов массива */.
public static void main( String [] mainArg ) {.
double a ED = getArrayO;.
//Пусть I - инвариант ... (раздел 4 главы 7). Устанавливаем I. int i =0; int k=0;.
boolean validityM = checkAssertion( i, k, a ); //Проверка инварианта.
//Применение тестирования //инвариантов к шах().
//Следующий код поддерживает I истинным (раздел 4 главы 7). while( il=a.length-l) {
i:.
if( a[i]>a[k] ) k = i;.
validityM = validityM && checkAssertion(i, k, a ); //Проверка инварианта.
//Применение тестирования //инвариантов к тахО.
}.
//Сообщение о результатах тестирования.
System.out.println( "Первый наибольший элемент массива равен" + а[к] + "и имеет номер" + к );.
System.out.println( "Ивариант имеет значение:" + validityM ); }.
Современные языки программирования либо имеют встроенные возможности тестирования инвариантов (С++ и Eiffel), либо позволяют построение функций тестирования инвариантов программному инженеру или третьей стороне.
8.2.6.1. Ограничения проверки автоматических инвариантов.
У проверки инвариантов есть свои ограничения. Например, инварианты, использованные в примере НОД в разделе 7.4, невозможно проверить в существующей форме с помощью блока проверки утверждений. Вспомните, что инвариантное утверждение, использовавшееся для доказательства, имело вид:.
НОД(апХ, aY) = НОД(х, у).
С точки зрения выполнения, это затрагивает основное вычисление (наибольшего общего делителя), которое мы пытаемся реализовать! С другой стороны, люди могут использовать свое понимание НОД для убеждения друг друга (и самих себя) в корректности кода.
8.2.7. Использование случайных величин в тестировании.
Вообще говоря, мы используем случайные входные данные при тестировании с целью избежать возникновения необъективных экземпляров тестов. Например, если мы хотим оценить мнение населения о политическом деятеле, мы выбираем представителей населения случайным образом. Здесь случайный выбор применяется определенным образом: сначала определяется тип теста, а затем применяется случайная выборка для получения объективных тестовых данных. То же справедливо и для программного тестирования. Когда выбран тип теста (например, рассмотрение решений) и определены границы данных, для возможных входных данных существует некоторая свобода. В идеале эти входные данные должны выбираться случайным образом.
В качестве примера представьте себе выполнение рассмотрения решений для блок-схемы из раздела 8.2.4. Предположим, мы хотим, чтобы ответом на вопрос «Имя слишком длинное?» было «Да». Существует большое множество данных, для которых это будет справедливо, и именно из этого множества и следует в идеале случайным образом выбирать тестовый пример. Один из способов — выбрать каждое имя, как указано далее.
Выбрать целочисленное i больше, чем maxNumCharsInNameO, случайным образом. Для; - 0...г выбрать случайным образом букву и установить в name[j] эту букву. В качестве второго примера представьте выполнение тестирования граничных значений для программы, проверяющей, являются ли четыре вещественных числа из последовательности х1, х2, хЗ и х4 допустимыми значениями для эксперимента. Допустим, что допустимыми будут значения, удовлетворяющие неравенствам:.
-5
< х1
< 10 х1 + х2
< хЗ хЗ >
х4.
Для граничного тестирования нам необходимо выбрать тестовые данные со следующими ограничениями:.
х1 —5 (недопустимо); х1 = 10 х1 + х2 = хЗ хЗ = х4.
Обратите внимание, что переменная х1 должна иметь только одно из двух значений, хЗ определена сразу после того, как выбраны х1 и х2, а х4 определяется через хЗ. Поэтому мы должны выбрать из бесконечного множества значений х2, что мы и делаем случайным образом во избежание предвзятости.
8.3. Планирование модульных тестов.
Систематический подход в тестировании необходим, поскольку число потенциальных модулей, нуждающихся в тестировании, обычно очень велико. Достаточно легко сказать, что «каждая часть работы должна быть протестирована», однако эта фраза несет в себе мало смысла, поскольку на этап тестирования выделяется лишь ограниченное количество ресурсов. Поэтому цель заключается в нахождении как можно большего количества ошибок как можно более серьезного уровня в рамках имеющихся ресурсов.
ОДИН ИЗ СПОСОБОВ СПЛАНИРОВАТЬ МОДУЛЬНОЕ ТЕСТИРОВАНИЕ -.
1.
Определите принципы модульного тестирования.
♦ Назначить самого разработчика ответственным?.
♦ Предоставить проверку другим?.
♦ Предоставить проектирование и проверку другим?.
2.
Определите, что, где и как документировать.
♦ Индивидуальная документация?.
♦ Как и где внедрять в другие типы тестирования?.
♦ Внедрить ли в формальные документы?.
♦ Использовать ли инструменты и тестовые утилиты?.
3.
Определите объемы модульного тестирования заранее:.
♦ не тестируйте просто «пока время не кончится»;.
♦ расставляйте приоритеты, чтобы важные тесты точно были проведены.
4.
Определите, как и где получить тестовые входные данные.
5.
Оцените необходимые ресурсы.
♦ Используйте имеющиеся данные предыдущих проектов, если это возможно.
6.
Организуйте учет времени, подсчет ошибок, фиксацию их типа и источника.
1. Определите принципы модульного тестирования. Первый вопрос заключается в определении того, какие модули мы будем рассматривать и кто будет их тестировать.
Для объектно-ориентированных проектов обычная организация модульного тестирования заключается в тестировании методов каждого класса, затем классов каждого пакета, затем пакета в целом. В нашем примере мы будем сначала тестировать классы в каждом пакете, а затем перейдем к программным пакетам, поскольку тестирование последних зависит от тестирования первых.
Тестирование модуля в идеале планируется и выполняется человеком, не участвовавшим в разработке. Модульное тестирование в реальной жизни иногда планируется и исполняется организацией контроля качества. Хотя достоинством такого подхода является независимость тестирования, в этом случае от инженеров организации контроля качества требуется понимание проекта в деталях. Некоторые организации-разработчики не поддерживают эту возможность и поэтому требуют от контролирующей качество организации только тестирования высокого уровня. Модульное тестирование часто остается за группой разработчиков и выполняется по их собственному усмотрению. В любом случае тесты делаются доступными для инспектирования и для возможного внедрения более высокого уровня. В некотором смысле независимость организации контроля качества может быть заменена перекрестным тестированием, выполняемым самими разработчиками, когда они тестируют модули друг друга.
2.
Определить способ документирования модульных тестов. Документирование модульных тестов состоит из тестовых процедур, входных данных, кода, исполняющего тест, и выходных данных. Модульные тесты можно упаковывать вместе с кодом либо в отдельных документах. Достоинство упаковки тестов вместе с кодом заключается в удобстве. Эта техника проиллюстрирована в разделе 8.4.2.1. Недостатком является увеличение объема кода. Можно использовать предварительную компиляцию для урезания тестового кода перед компиляцией модуля.
Кроме того, для выполнения модульных тестов используются драйверы тестов и утилиты. Они также документируются для будущего использования. Например, в нашем учебном проекте используется следующая служебная тестовая функция:.
public static void reportToFile (.
FileWriter FileWriterP. String TestDescriptionP, String CorrectOutputStringP. String ActualOutputStringP ).
3.
Определите объемы модульного тестирования. Поскольку «протестировать все» невозможно, границы тестирования должны быть сознательно определены. Например, если банковская программа состоит из снятия, депозитов и запросов, модульное тестирование могло бы указать, что каждый метод должен быть протестирован с равным количеством разрешенных, граничных и запрещенных данных или, возможно, методы снятия и депозита должны тестироваться в три раза больше, чем методы запросов, и т. д. В общем случае методы, изменяющие состояние (значения переменных), обычно тестируются больше других. Границы того, что относится к модульному тестированию, также должны быть определены. Например, входит ли сюда тестирование пакетов, или оно должно относиться к другому типу тестирования (глава 9)?.
Разработчики заранее определяют границы тестирования, в том числе и момент, когда процесс тестирования должен быть завершен. Например, следует ли тестировать каждый модуль одинаковое количество времени или до обнаружения первых трех ошибок? Критерии остановки тестирования обсуждаются далее в разделе 8.5.2.
Вспомните, что наша идея тестирования заключается в выполении тестов, которые с наибольшей вероятностью помогут выявить ошибки. Расставляя приоритеты тестам в соответствии с вероятностью обнаружения ими ошибок, мы тем самым стараемся оптимизировать время, отведенное на тестирование. Приоритетный подход зависит от тестируемого модуля. Основным источником ошибок являются циклы. Сюда же относятся граничные значения и интерфейсы. В последней категории тесты, затрагивающие части модулей, такие как функции, обычно изобилуют потенциальными ошибками, поскольку каждая часть ожидает получить данные в определенной форме, а эти ожидания часто не выполняются.
4.
Определите, как и где получать тестовые входные данные. Мы обсудили разрешенные, граничные и запрещенные входные тестовые данные. Также необходима некоторая случайная генерация данных. По возможности используются инструменты, генерирующие входные тестовые данные посредством анализа исходного кода и обнаружения граничных значений данных и ветвления. Вдобавок значительный объем тестовых данных можно получить из предыдущих версий программы, стандартных источников, промышленных контрольных задач и т. д. Все это документируется для будущих ссылок и повторного использования.
5.
Оцените необходимые ресурсы. Как всегда при планировании, мы определяем человеко-месяцы и время, необходимое для выполнения модульного тестирования. Основным источником этой оценки являются накопленные данные. Хотя модульное тестирование часто связано с процессом разработки, отдельное его выполнение предоставляет бесценную информацию.
6.
Организуйте учет времени, а также учет дефектов, их типа и источника. Участвующие инженеры определяют четкую форму, в которой они будут вести учет затраченного на модульное тестирование времени, учет ошибок и их типов. Полученные данные используются для утверждения состояния программы и предсказания конечного качества работы и сроков окончания. Данные также становятся частью учетных записей истории проекта.
Следующие два раздела представляют примеры планирования модульных тестов на уровнях методов и классов. Пример в конце главы показывает получившийся код.
8.4. Контрольные таблицы и примеры тестирования методов.
8.4.1. Модульное тестирование методов.
Хэмфри [48] советует использовать для выполнения тестирования методов контрольные таблицы.
ОДИН ИЗ СПОСОБОВ ВЫПОЛНИТЬ ТЕСТИРОВАНИЕ МЕТОДОВ ПО ХЭМФРИ (1) —.
1.
Проверить работу при нормальных значениях параметров. Тест «черного ящика», основанный на требованиях к модулю.
2.
Проверить работу при граничных значениях параметров. «Черный ящик».
3.
Проверить работу при значениях параметров вне разрешенного диапазона.
4.
Убедиться, что выполняются все инструкции. Рассмотрение утверждений.
5.
Проверить все пути, в том числе обе ветви каждого узла. Рассмотрение решений.
6.
Проверить использование всех вызванных объектов.
7.
Проверить обработку всех структур данных.
8.
Проверить обработку всех файлов.
ОДИН ИЗ СПОСОБОВ ВЫПОЛНИТЬ ТЕСТИРОВАНИЕ МЕТОДОВ ПО ХЭМФРИ (2) —.
9. Проверить нормальное завершение всех циклов. (Часть доказательства корректности.).
10.
Проверить непредусмотренное завершение всех циклов.
11.
Проверить нормальное завершение всех рекурсий.
12.
Проверить непредусмотренное завершение всех рекурсий.
13.
Проверить обработку всех условий ошибок.
14.
Проверить синхронизацию и расчет времени.
15.
Проверить аппаратные зависимости.
Методы принадлежат одной из двух категорий (рис. 8.11). Первая категория соответствует требованиям, предъявленным к программе. Вторая категория работает с классами и методами, добавленными для формирования проекта.
Рис. 8.11. Связь тестов с требованиями и проектированием
Модульное тестирование включает в себя автономное тестирование по возможности каждого метода на точное соответствие требованию, сформулированному в SRS. Другими словами, мы проверяем, что метод удовлетворяет своему требованию. В данных условиях это представляет собой тестирование «черного ящика». Также используется и тестирование «белого ящика» применительно к каждому методу (например, для рассмотрения утверждений и решений).
Для методов, возникающих из проекта, мы часто не располагаем явно сформулированными требованиями, в соответствии с которыми можно было бы выполнить тесты. Примером теста, происходящего из проекта, является тест класса СостояниеИгры. Это класс, введенный для проектирования нашей видеоигры: ни одно из исходных требований не касается конкретно этого класса. В идеале для всех классов проекта при создании таковых должны быть написаны отдельные требования. Когда такие отдельные требования не написаны, как часто случается, тестовые варианты следует разрабатывать в соответствии с предполагаемой функциональностью класса.
8.4.2. Пример теста метода.
План для выполнения тестирования модуля метода в случае проекта Встреча может быть таким.
Для каждого класса разрабатывается метод, тестирующий методы. Он выполняет каждый метод класса с различными значениями параметров. В методе тестирования используется вспомогательный метод reportToFileQ класса TestExecution.
Метод reportToFileQ имеет следующие параметры:.
чение>.
Для получения тестовых данных по каждому методу мы вручную определяем границы параметра, затем вручную выбираем хотя бы одно значение внутри границ, на границах и за разрешенными границами.
Пример, приведенный ниже, показывает, как выбираются значения для одного метода. Код в конце главы демонстрирует применение плана тестирования для класса ПерсонажВстречи. Как будет видно далее, разработка систематических тестовых вариантов даже для этих случаев не так проста.
Мы будем тестировать следующее D-требование видеоигры Встреча:.
3.2.ПВ.1.2. У каждого игрового персонажа имеется одинаковый набор характеристик. Каждая характеристика представлена неотрицательным числом с десятичной запятой и как минимум одной цифрой в дробной части. Все они одинаково инициализируются, так что сумма значений всех характеристик равна 100. Значение характеристики не может быть в промежутке 0-0,5.
В первой версии будут такие характеристики, как сосредоточенность, ум, терпение, выносливость и сила.
Подходящий набор тестов для метода adjustQuality( String qualityP, float valueP ) приводится ниже. Этот метод устанавливает значение характеристики qualityP равным valueP и настраивает остальные характеристики так, чтобы их взаимные соотношения оставались прежними. Чисто случайная комбинация разрешенных и неразрешенных значений qualityP и valueP не будет подходящим началом разработки набора тестов: во-первых, нужно обдумать диапазон параметров. В каждом диапазоне нужно определить граничные, внешние и лежащие внутри диапазона данные. Мы стремимся использовать системный подход, что обычно означает поиск представителей разбиения равнозначности. Типичный результат систематического разбиения входных данных на зоны эквивалентности показан в табл. 8.1.
Таблица 8.1. Разбиение значений на диапазоны для модульного тестирования Модульный тест Применили к.
сосредоточенности выносливости.
Возвращает нуль?
1. Внутри
Нет: Тест 1.1
Тест 1.1.1
диапазона
Да: Тест 1.2
Тест 1.2.1
2. На
2.1. Вызвали
Нет: Тест 2.1.1
Тест 2.1.1.1
границах
с нулевым значением.
2.2. Вызвали со значением 100
Да: Тест 2.1.2
3. Вне
3.1. Вызвали
диапазона
с параметром
< 0
Второй уровень разбиения можно определить исходя из того, может ли значение характеристики оказаться нулевым в результате применения метода adjustQualityO. Третий уровень разбиения показан в табл. 8.1, а результаты перечислены ниже.
1. Тестовые варианты с параметрами из диапазона.
1.1. adjustQualityO возвращает ненулевое значение:.
1.1.1. Параметр характеристики = «сосредоточенность».
Входные данные (в идеале следует выбрать здесь число от 0 до 100, чтобы в сумме получалось число менее 100): Сосредоточенность: 20;.
Выносливость: 20 (замечание: Л всех значений без сосредоточенности); Ум: 20; Терпение: 20; Сила: 20.
Выполнение: adjustQuality( «сосредоточенность», 10) (В идеале это значение выбирается случайным из внутреннего диапазона, гарантированно дающего ненулевое значение сосредоточенности). Ожидаемый результат:.
Сосредоточенность: 20 + 10 = 30;.
Выносливость: 70 / 4 = 17,5 (замечание: % всех значений без сосредоточенности); Ум: 70 / 4 - 17,5; Терпение: 70 / 4 = 17,5; Сила: 70 / 4 = 17,5.
Тесты 1.1.2, 1.1.3, ... аналогичны, используют другие характеристики вместо сосредоточенности. 1.2. adjustQuality() возвращает нулевое значение:.
(В идеале использовать случайную величину из диапазона аналогично описанному выше способу.).
1.2.1. Параметр характеристики = «сосредоточенность».
Входные данные:.
Сосредоточенность: 20;.
Выносливость: 20 (замечание: V* всех значений без сосредоточенности) Ум: 20; Терпение: 20; Сила: 20.
Выполнение: adjustQuality(«сосредоточенность», 99).
Ожидаемый результат: Сосредоточенность: 99; Выносливость: 0 (Л заменена на нуль); Ум: 0 (? заменена на нуль); Терпение: 0 (Vi заменена на нуль); Сила: 0 (Л заменена на нуль). Тесты 1.2.2, 1.2.3, ... аналогичны, используют другие характеристики вместо сосредоточенности.
2. Тестовые варианты с параметрами на границах диапазона.
2.1. adjustQualityO вызван с нулевым значением одного параметра: 2.1.1. Результаты при ненулевых значениях: 2.1.1.1. Параметр характеристики = «сосредоточенность».
Входные данные:.
Сосредоточенность: 0; Выносливость: 25 Ум: 25; Терпение: 25; Сила: 25.
Выполнение: adjustQuality(«выносливость», 74).
Ожидаемый результат: Сосредоточенность: 0; Выносливость: 99; Ум: 0(1/3 заменена на нуль); Терпение: 0(1/3 заменена на нуль); Сила: 0 (1/3 заменена на нуль). Тесты 2.1.1.2, 2.1.1.3, ... аналогичны Тесты 2.2, 2.3, ... соответствуют другим границам. Например: 2.2.N. adjustQualityO вызван со значением, равным текущему: Входные данные:.
Сосредоточенность: 0; Выносливость: 25 Ум: 25; Терпение: 25; Сила: 25.
Выполнение: adjustQuality(«выносливость», -25).
Ожидаемый результат: Сосредоточенность: 0; Выносливость: 0; Ум: 33; Терпение: 33; Сила: 33.
3. Тестовые варианты с параметрами вне диапазона.
3.1. Выше верхней границы значений параметров.
3.1.1. Параметр характеристики = «сосредоточенность».
Входные данные:.
Сосредоточенность: 20; Выносливость: 20; Ум: 20; Терпение: 20; Сила: 20.
Выполнение: adjustQuality(«сосредоточенность», 81).
Ожидаемый результат:.
Сообщение об ошибке, докладывающее о том, что метод adjustQualityO был вызван с неразрешенным параметром.
Сосредоточенность: 100 (20 + 81 установили на 100); Выносливость: 0 (после установки сосредоточенности не осталось очков-жизней для распределения между остальными характеристиками); Ум: 0;.
Терпение: 0; Сила: 0.
Тесты 3.1.2, 3.1.3, ... аналогичны.
Тесты 3.2, 3.3, ... аналогичны тесту 3.1.
3.N. Ниже нижней границы значений параметров.
3.N.1 Параметр характеристики = «сосредоточенность».
Входные данные:.
Сосредоточенность: 20; Выносливость: 20; Ум: 20; Терпение: 20; Сила: 20.
Выполнение: adjustQuality( «сосредоточенность», -21).
Ожидаемый результат:.
Сообщение об ошибке, докладывающее о том, что метод adjustQualityO был вызван с неразрешенным параметром. Сосредоточенность: 0 (20 - 21 установили на 0); Выносливость: 25 (100/4); Ум: 25 (100/4); Терпение: 25 (100/4); Сила: 25 (100/4). Остальные тесты создаются аналогично.
8.4.2.1. Тестирование всех методов класса.
В качестве примера рассмотрим класс GameCharacter (ПерсонажИгры) пакета Characters (Персонажи). На данный момент это единственный класс данного пакета, и он уже рассматривался на этапе создания архитектуры.
Мы покажем код внутри класса GameCharacter, который будет выполнять тестирование метода setNameO. Это можно организовать так, как показано далее. (Обозначение //ps указывает на фрагмент для автоматического выделения псевдокода.) В примере в конце главы представлен полный тест метода.
/*.
* GameCharacter.java.
*/.
package FrameworkRPG.Characters;.
import TestUtilities.*; //внутренний тестовый пакет import java.util.*;.
import java.io.*; /**.
* Персонаж ролевой игры.
* (Pauthor Eric Braude.
* (aversion 0.1, 7/14/98.
*/.
public abstract class GameCharacter {.
// АТРИБУТЫ ---------------------.
/** Имя игрового персонажа; при инициализации null */.
private String namel;.
// МЕТОДЫ ----------------------.
/** Ни одно имя не будет длиннее этой величины */.
public final static int 1imitOfNameLength() {.
return 100; }.
/** Для регистрации */.
protected void displayO.
r i.
System.out.print!n( «Game character » + namel + « displayed» ): }.
/** Доступ к namel. «defaultName» назначается при первом.
* доступе.
*/.
public String getNameO.
{.
if( namel == null ) //раньше не было доступа setName( «defaultName» );.
return namel; }.
/** Подклассы должны объявлять ограничение на размер имен.
* персонажей.
*/.
protected abstract int maxNumCharsInNameO;.
/** Устанавливает значение namel равным nameP, если длина.
* не превосходит maxNumCharsInNameO; в противном случае.
* обрезается.
* Наследники должны использовать это для setNameC String ),.
* но не перезаписывать.
* Ррагаш nameP: предлагаемое имя для namel.
*/.
protected final void setName( String nameP ) {.
//ps IF параметр строки OR maxNumCharsInName() не имеет смысла.
if( ( nameP == null ) || ( maxNumCharsInName().
limitOfNameLengthO ) ) {.
// Установить в качестве имени значение по умолчанию // и показать его в системном окне.
namel = new String( «defaultName» ): System.out.println.
(«defaultName selected by GameCharacter.setName()»);.
}.
// ps ELSE else.
//ps IF строковый параметр слишком длинный if( nameP.length() > maxNumCharsInName() ) //ps обрезать no maxNumCharsInNameO namel = new String ( nameP.getBytes().
0, maxNumCharsInName() );.
else.
//ps ELSE назначить имя параметру namel = new String( nameP );.
}.
/** Протестировать этот класс */.
public static void main( String[] args ) {.
//Получить имя файла для результатов теста и //запустить тест String outputFileNameM;.
if( args == null || args.length == 0 ) {.
System.out.println( «Using output.txt as default for rest results." ): OutputFileNameM = «output.txt»:.
}.
else.
outputFileNameM = argsCO]:.
try {.
testGAmeCharacterMethods( outputFileNameM );.
}.
catch( IOException е ) {.
System.out.println( e );.
}.
}.
/** Протестировать все методы этого класса */.
public static void testGameCharacterMethods(.
String destinationP ) throws IOException.
{.
//подготовка к тесту: FileWriter outM = new FileWriter(.
new File( destionationP ) );.
// поскольку это абстрактный класс, нужно создать один // конкретный для тестирования.
class TempGameCharacter extends GameCharacter {.
public int maxNumCharsInNameO {.
return 20;.
}.
}.
TempGameCharacter с = new TempGameCharacterO;.
System.out.println ( «Test for Gamecharacter class with.
maxNumCharsInNameO =" + c. maxNumCharsInNameO );.
System.out.println( «GameCharacter test results on» + DestinationP + «п» );.
}.
// Тесты для SetNameO-------------------.
/** SetNameO 1. Разбиение равнозначности: номинальное, * разрешенное имя. */.
c.setName( «Наггу» );.
TestExecution.reportToFi1е( outM, «SetName Test 1.1: Non-null string", c.namel, «Harry» );.
/* SetNameO 2. Анализ граничных значений */ c.setName( «X» ); //минимальная разрешенная длина TestExecution.reportToFi1е( outM, «SetName Test 2.1: Min non-null string", c.namel. «X» );.
Планы модульного тестирования для getNameO, displayO и getCharacterO выполняются аналогично. В примере в конце главы показан тест для методов класса ПерсонажВстречи.
8.5. Контрольные таблицы и примеры тестирования классов.
После тестирования отдельных методов класса мы можем продолжать тестировать класс в целом. Тестирование класса представляет собой совместное выполнение методов класса или тестирование объектов класса при определенных событиях, например событиях мыши. Абсолютно случайный набор комбинаций методов и в этом случае, скорее всего, приведет к пустой трате времени и не закроет пробелы в рассмотрении класса. Однако существует несколько дополнительных способов тестирования классов.
ОДИН ИЗ СПОСОБОВ ВЫПОЛНИТЬ МОДУЛЬНОЕ ТЕСТИРОВАНИЕ КЛАССОВ -.
1.
Испытывайте комбинации методов:.
♦ обычно из 2-5 методов;.
♦ выберите сначала наиболее общие последовательности;.
♦ учтите последовательности, которые могут привести к ошибке;.
♦ результирующие значения атрибутов требуется подсчитывать вручную.
2.
Фокусируйте модульные тесты на каждом атрибуте.
♦ Инициализируйте атрибут, а затем запускайте последовательности методов, влияющих на него.
3.
Проверяйте, что инвариант каждого класса не изменяется:.
♦ проверьте, что инвариант истинен при начальных значениях;.
♦ выполните последовательность (например, ту же, что и на шаге 1);.
♦ проверьте, что после этого инвариант остается истинным.
4.
Проверьте, что объекты переходят из ожидаемых состояний в ожидаемые:.
♦ спланируйте последовательность переходов состояний;.
♦ установите объект в исходное состояние, присвоив значения переменным;.
♦ обеспечьте выполнение первого события и проверьте выполнение перехода, и т. д.
План выполнения тестирования на уровне класса для примера видеоигры Встреча может быть таким:.
Для каждого класса Xразрабатывается метод testXClassQ, выполняющий методы в различной последовательности. testXClassQ можно выполнять в методе main() или во внешнем для этого класса коде. Последовательности методов разрабатываются посредством техник, изложенных на рис. №№.
8.5.1. Пример теста комбинации методов.
Каждый тест комбинации методов состоит из последовательности вызовов функций. Например, в классе ПерсонажВстречи следующие методы будут тестироваться последовательно:
Сокращение
Прототип метода
aq
adjustQuality( String qualityP, float qualityValueP )
d
deleteFromEncounterCharacters( EncounterCharacter EncounterCharacterP )
ge
EncounterCharacter getEncounterCharacter( String nameP )
gq
float getQualityValue( String qualityP )
gs
float getSumOfQualities()
gt
float getTolerance()
io
int indexOf( String qualityP ) throws Exception
lnsertlntoEncounterCharacters( EncounterCharacter encounterCharacterP )
m
int maxNumCharslnNameO
sq
setQuality( String qualityP, float valueP )
Мы концентрируем наши тестовые ресурсы двумя способами. Мы определяем:.
♦ последовательности, которые, вероятно, будут чаще всего использоваться;.
♦ последовательности, которые наиболее подвержены возникновению ошибок. Это приводит к нахождению наибольшего числа ошибок на каждый затраченный доллар.
Приведенные ниже последовательности часто встречаются:.
♦ ge-aq-gs // получить персонаж — настроить характеристики — получить сумму характеристик;.
♦ ge-sq-aq-gq // получить персонаж — установить значение характеристики — настроить характеристики — получить характеристику.
Вас не должно пугать бесконечное количество возможных тестов, которые можно запустить даже для несложных программ. (Ввиду этого инспектирование и доказательство корректности не так дороги, как они могут показаться на первый взгляд.) На длину последовательностей методов накладывается ограничение: поскольку у нас десять методов, мы ограничим максимальную длину числом 10. Подход отбраковки может оказаться полезным для определения общих последовательностей. Данную последовательность методов можно классифицировать как наиболее или наименее подверженную возникновению ошибок. В противном случае последовательность относят к категории «ни то, ни другое». Все «наиболее вероятные» последовательности тестируются. Также тестируется как можно большее число последовательностей из категории «ни то, ни другое». Последовательности из «наименее вероятных» тестируются, если на это есть время.
Поскольку процесс настройки значений характеристик относительно сложен, ниже приведен пример последовательности, которая с большой вероятностью может порождать ошибки:.
♦ ge-sq-aq-sq-aq-gq /* получить персонаж — установить значение характеристики — настроить характеристики — установить значение характеристики — настроить характеристики — получить характеристику */.
8.5.2.
Тесты атрибутов.
Тесты атрибутов разрабатываются посредством фокусировки на отдельных атрибутах и предсказания воздействия на них различных методов из последовательности, например setBalance(lOO); addToBalance(70); getBalanceO. Мы выполняем последовательность и проверяем, чтобы получившееся значение атрибута оказывалось таким, какое мы ожидали. В результате тесты атрибутов представляют собой тесты последовательностей методов с проверкой атрибутов.
8.5.3.
Тестирование инвариантов класса.
Как рассказывалось в главе 7, инварианты класса являются ограничениями на атрибуты класса, которые должны сохраняться истинными в соответствующих точках выполнения. Тесты инвариантов классов состоят из проверки истинности каждого инварианта посредством выполнения последовательности методов и проверки сохранения истинности инварианта. Например, один из инвариантов класса ПерсонажВстречи заключается в том, что сумма значений характеристик должна быть менее 100. Ниже приведен фрагмент кода, который проверяет этот инвариант. Он взят из метода testEncounterCharacterClass класса ПерсонажВстречи.
/*......
Проверить инвариант «сумма значений характеристик объекта персонажа chapacterP те технику «черного ящика», задачей которой является уменьшение числа тестовых вариантов.
П8.4°. Тестируем выборочно.
1.
Сформулируйте практический критерий выбора входных тестовых данных.
2.
Вообще говоря, какие виды входных значений обычно приводят к большинству общих ошибок?.
П8.5
. Приведите пример программы со следующими свойствами. Программа должна реализовывать простую блок-схему, но осуществляет это некорректно. Существуют входные данные к программе, которая выполняет каждую строку программы и возвращает корректный ответ. Раздел 8.2.4 показывает решение этого вопроса.
П8.6". Приведите пример программы в форме блок-схемы с единственной точкой ветвления. Создайте дефектную программу со следующим свойством. Существуют входные данные, исполняющие каждую ветвь программы. Ответ на этот вопрос содержится в разделе 8.2.5.
П8.7". Какое утверждение вы бы стали проверять при компиляции, находящей первый минимум в массиве а размером 100?.
П8.8". Назовите 4-6 шагов в планировании модульного тестирования. Ответом на этот вопрос является раздел 8.3.
П8.9". Назовите 3 теста «черного ящика» для функций (методов).
П8.10
. Назовите 6-12 тестов «белого ящика» для функций (методов).
П8.1Г. Приведите код, проверяющий, удовлетворяется ли инвариант I в классе ПерсонажВстречи. I = «все значения характеристик неотрицательны». Обратитесь к переменным в фрагменте кода ПерсонажВстречи в разделе 8.5.3.
П8.12". Опишите тестирование на основе состояний. Ответ на этот вопрос вы найдете в разделе 8.5.4.
Упражнения в команде.
К8.1. («Модульное тестирование») Выполните полное модульное тестирование двух основных методов вашей программы. Опишите, сколько времени члены вашей команды отдельно и все вместе потратили на разработку каждой части этих тестов и как этот процесс можно было бы улучшить.
Критерии оценки.
1.
Степень четкости формулировки плана («Отлично» — очень четкий план).
2.
Степень, в которой в план и тест были включены все существенные аспекты модульного тестирования («Отлично» — все важные рассмотрения, упомянутые в этой главе).
3.
Реализм самооценки и план улучшения («Отлично» — очень конкретные данные по результатам и конкретные изменения для улучшения процесса).
Общие упражнения.
08.1. Напишите код для класса Счет с атрибутом _баланс, методами доступа и методом добавить(). Исходите из того, что Счет имеет состояния Платежеспособный, Пустой и Задолженность, и они реализованы с использованием образца проектирования State. Напишите полный набор модульных тестов для класса Счет, в том числе и тесты на основе состояний.
Ответы.
П8.1. Функциональные тесты проверяют каждую отдельную функцию или метод. Тесты класса проверяют каждый класс. Затем идут тесты пакетов. Комплексные тесты проверяют части программы. Наконец, системные тесты проверяют программу в целом.
П8.3. Разбиение равнозначности уменьшает число вариантов тестов посредством разбиения множества возможных входных данных на отдельные подмножества. Эти подмножества выбираются так, чтобы при удачном прохождении теста с одним набором данных все возможные входные данные из этого подмножества тоже скорее всего были удачны в тестировании.
П8.4.
1.
Проверить входные данные, которые наиболее вероятно дадут ошибку.
2.
Дефекты обычно появляются чаще всего в граничных случаях.
П8.7. Утверждение, которое нужно удовлетворить, является следующим набором из четырех утверждений.
О.
AND.
О.
AND // первый минимум - ^-элемент.
[ либо к = О.
либо [а[и]> a[k] для и = 0,1,..., k-1] //значения больше у всех элементов с индексом менее k.
AND.
[а[и]меньших чисел.
П8.9. Тесты функций «черного ящика» можно получить с помощью следующих тестовых данных:.
♦ из диапазона;.
♦ на границе диапазона;.
♦ вне диапазона.
П8.10.
1.
Проверить все пути, в том числе обе ветви каждого ветвления.
2.
Убедиться, что все инструкции были выполнены.
3.
Проверить использование всех вызванных объектов.
4.
Проверить обработку всех структур данных.
5.
Проверить обработку всех файлов.
6.
Проверить правильное завершение всех циклов.
7.
Проверить неправильное завершение всех циклов.
8.
Проверить правильное завершение всех рекурсий.
9.
Проверить неправильное завершение всех рекурсий.
10. Проверить обработку всех условий ошибок.
И. Проверить время и синхронизацию. 12. Проверить все аппаратные зависимости.
П8.11. Приведенный ниже код проверяет инвариант класса, согласно которому все значения характеристик должны быть неотрицательными.
// . . .
// Проверить инвариант е.equalValue[1] >= О boolean truthM = true;.
for( int i = 0; i
< qualityTypeS.length; ++i ).
//Установить truthM в значение false, если любое //вхождение в e.qualValue[i] не >
= О truthM = truthM && (е.qua 1Va1ueI[i] >= 0); // сравнение ожидаемого и полученного результатов TestExecution.pri ntReportToFi1e (.
out, // test log file.
«Проверить инвариант e.equalValue[i] >= 0», //описание теста truthM, // полученное значение true // ожидаемое значение.
);.
Пример. Индивидуальная программная документация (PSD) на Java для класса EncounterCharacter (ПерсонажВстречи), часть 2.
[Примечание для студентов. Ниже приведена вторая часть документа, описывающего индивидуальную программную документацию для EncounterCharacter (ПерсонажВстречи). Формат этого документа взят из IEEE-стандарта для документации по тестированию программы.
То, как выполняется модульное тестирование в этом примере, является лишь одним из многочисленных способов. Например, альтернативным путем было бы выполнение тестов через статические самотестирующие методы из внешнего объекта. Этот объект можно сделать так, чтобы он выполнял несколько модульных тестов и посылал результаты в конкретные выходные файлы, следуя инструкциям в тестовом скриптовом файле.].
4. Модульное тестирование для класса EncounterCharacter (ПерсонажВстречи).
4.1. Спецификация проекта тестирования.
Модульное тестирование для EncounterCharacter (ПерсонажВстречи) состоит из двух открытых методов:.
♦ testEncounterCharacterMethodsO проверяет каждый метод по очереди.
♦ testEncounterCharacterClassO проверяет последовательности методов.
Эти методы можно выполнить из метода EncounterCharacter mainO с помощью внешнего объекта.
4.2.
Спецификация совокупности тестовых данных.
В testEncounterCharacterMethodsO и testEncounterCharacterClassO встраиваются совокупности тестовых данных для EncounterCharacter.
[Примечание для студентов. Для простоты этот модульный тест содержит данные внутри метода. Однако, обычно входные данные и ожидаемый результат извлекаются из файла.].
4.3.
Спецификация процедуры тестирования.
Модульные тесты для EncounterCharacter инициируются посредством выполнения метода mainO. Параметр, передающийся в mainO, определяет файл, в который записываются результаты.
[Примечание для студентов. Это простая процедура, однако она значительно усложняется, когда требуется взаимодействие исходных файлов и пользователя. Например, это будет в модульном тестировании класса EncounterGame (ИграВстреча ).].
4.4.
Документация результатов тестирования.
Документация результатов тестов состоит из журнала испытаний, отчета о тестах и итогового тестового отчета.
4.4.1.
Журнал испытаний.
[Примечание для студентов. Учет результатов теста. См. пример ниже.] Содержится в файле: EncounterCharacter_Test_Log_day_month_year.doc.
4.4.2.
Отчет о тестах.
[Примечание для студентов. Любые события, требующие внимания во время тестов. См. пример ниже.].
Содержится в файле: EncounterCharacter_Test_Incident_day_month_year.doc.
4.4.3.
Итоговый тестовый отчет.
Содержится в файле: EncounterCharacter_Test_Summary_Report_day_month_year.doc.
Пример журнала испытаний (PSD, раздел 4.4.1) EncounterCharacter_Test_Log_26_Jul_1999.
Протестированные методы:.
»»>GetCharacter тест 1: номинальное значение ««
<.
querty.
querty.
»»>
GetCharacter тест 2: значения внешнего параметра««
<.
defaultName.
defaultName.
»»>
EncounterCharacter тест 3: граничные значения параметра««
<.
123456789012345.
123456789012345.
Ожидается одно имя каждого персонажа.
querty.
defaultName.
123456789012345.
»»>
indexOf() тест 1: правильное название характеристики««
< фактическое целое число = ожидаемому целому числу »»>
indexOf() тест 2: правильное название характеристики««
< фактическое целое число = ожидаемому целому числу »>
»setQuality() тест 1: номинальное значение««
< фактическое вещественное число = ожидаемому вещественному числу »»>
setQuality() тест 2: номинальное значение««
< фактическое вещественное число = ожидаемому вещественному числу »»>
adjustQuality() тест 0: проверить то, что сумма значений равна Ю0фактическое вещественное число = ожидаемому вещественному числу »>»adjustQuality() тест 1: проверить то, что сумма значений равна 100 после изменения««
<.
фактическое вещественное число = ожидаемому вещественному числу »»>
adjustQuality() тест 2: проверить то, что значения изменились так, как нужно««
<.
фактическое вещественное число = ожидаемому вещественному числу »»>
adjustQuality() тест 3: проверить то, что маленькие значения обнуля-.
ются««
<.
фактическое вещественное число = ожидаемому вещественному числу »»>
adjustQuality() тест 4: проверить то, что сумма значений равна 100 после настройки««
<.
фактическое вещественное число = ожидаемому вещественному числу Тест класса:.
»»>
Классовый тест ge-aq-so««
<.
100,0.
100,0.
»»>
Классовый тест ge-aq-aq-gq-so: часть 1««
<.
20,9876.
20,9876.
»»>
Классовый тест ge-aq-aq-gq-so: часть 2««
<.
100,0.
100,0.
»»>
Классовый тест инварианта _qualValue[i]>=0««
<.
true.
true.
/Примечание для студентов. Пример журнала тестирования не показывает неудачные тесты. Их можно описать здесь, вынести в отдельный файл.].
Пример отчета о результатах теста (PSD, раздел 4.4.2) EncounterCharacter_Test_lncident_26_Jul_1999.doc.
Тестирование проводилось для версии 7.2.1 EncounterCharacter с использованием пакета TestUtilities версии 2.3. С первой попытки мы не смогли запустить тест. Мы считаем, что это было связано с тем фактом, что у нас на самом деле не было версии 2.3 TestUtilities. Когда мы перезагрузили этот пакет, тест прошел без проблем.
[Примечание для студентов. Здесь уместно упомянуть об ошибках, сделанных во время тестирования. Это уместно особенно в тех случаях, когда требуются действия пользователя, а производить перезапуск всего теста непрактично.].
Пример итогового тестового отчета (PSD, раздел 4.4.3).
EncounterCharacter_Test_Summary_26_Jul_1999.doc.
Этот тест проводился Джоном Джонсоном в 14.00 с использованием версии 1.1.6 виртуальной машины Sun. Тест прошли 100 % методов модульного теста. Эти методы были добавлены Э. Брауде в версию 6.5.2. Они будут дорабатываться в дальнейших версиях EncounterCharacter.
Пример исходного кода модульного теста.
Приведенный в листинге 8.3 код для класса EncounterCharacter содержит методы, тестирующие сами себя.
Класс TestExecution используется для выполнения модульного тестирования. Он содержит статический метод printReportToFileO, методы которого в нотации Javadoc приведены ниже.
*@pararn-Fi1eWriter-MecT0Hax0>
KfleHMe файла с отчетом *@param-Stri ng-описание теста ^param-int-ожидэемый корректный результат *@param-int-полученный результат.
* @return-void *@exception-HeT.
Здесь нет предусловий. Постусловием является требование того, чтобы файл был записан по соответствующему адресу, указанному во входном параметре FileWriter. Он должен содержать описание тестовых входных данных, ожидаемый результат и полученный результат — каждый четко указан. Код показан в листинге 8.3.
Листинг 8.3. Исходный код для модульного тестирования класса EncounterCharacter (ПерсонажВстречи).
/** Тестирование данного класса.
* @param argsP Расположение журнала тестирования методов относительно.
* журнала тестирования классов */.
public static void main( String[] argsP ) {.
// Файлы, в которых по умолчанию записываются выходные // данные тестов и фиксируется работа тестов String methodOutputFileNameM = «methodOutput.txt»;.
Листинг 8.3 (продолжение).
String classOutputFileNameM= «classOutput.txt»;.
11 Если входные данные некорректны, использовать // значения по умолчанию if( argsP != null && argsP.length == 2 ) { methodOutputFileNameM = argsP[0]; classOutputFileNameH = argsP[l];.
i l.
// 1. ВЫПОЛНИТЬ ТЕСТЫ. HE ТРЕБУЮЩИЕ УЧАСТИЯ ЛЮДЕЙ.
// Протестировать методы индивидуально, затем протестировать классы try.
{ testEncounterCharacterMethods( methodOutputFi1eNameM );.
testEncounterCharacterClass( classOutputFileNameM ); } catch( IOException eP ).
{ System.out.println( eP ); }.
//2. ВЫПОЛНИТЬ ТЕСТЫ. HE ТРЕБУЮЩИЕ УЧАСТИЯ ЛЮДЕЙ Frame[] imageTests = { //Показать совокупности тестовых данных.
new testCharacterImage( //Нет рисунка.
new EncounterCharacter( «GuyWithNoImage», null ) ). new testCharacterImage( //Есть рисунок.
new EncounterCharacter( «Elena», «elena.gif» ) ).
};.
for( int i = 0; i
< imageTests.length; i++) { //Показать окно каждого.
//теста.
imageTests[i].setSize(400, 250); //Подобрать размеры.
imageTestsCi].setLocatlon((i * 110 + 50) & 500.
(i + 60 + 25) % 200); imageTests[i].setVisible(true): imageTests[i].show();.
}.
try { //Пусть пользователь проанализирует окна.
Thread.currentThreadC).sieep(15*1000);.
} catch( Exception exc) { }.
for( int i = 0; i
< imageTests.length; i++) //Закрыть окна imageTests[i].dispose();.
System.exit(O);.
} /** Тестирование комбинации методов класса.
* (Эрагат destinationP Место записи результатов тестирован.».
* ^exception IOException В случае невозможности открытия или.
* доступа к destinationP */.
public static void testEncounterCharacterClass( String destinationP ).
throws IOException { /* Подготовка к тесту */.
PnntWriter outM=new PrintWriter( new FileOutputStream(destinationP) ); System.out.println(.
« EncounterCharacter class test results on » + destinationP + «п» ); /*.
* Будут тестироваться последовательности следующих методов:.
*.
* a. adjustQuality( String qualityP. float qualityValueP ).
* d. de1eteFromEncounterCharacters(.
EncounterCharacter encounterCharacterP ).
* ge. EncounterCharacter getEncounterCharacter( String nameP ).
* gq. float getQualityValue( String qualityP ).
* gt. float getTolerance().
* io. int indexOf( String qualityP ).
* ii. insertIntoEncounterCharacters(.
EncounterCharacter encounterCharacterP ).
* m. int maxNumCharsInNameO.
* sq. setQuality( String qualityP. float qualityValueP ).
* so. float sumOfQualitiesO.
* Часто встречаются следующие последовательности:.
* ge-aq-so.
* ge-sq-a-gq.
*.
* Ошибки наиболее вероятны в следующих последовательностях:.
* ge-aq-aq-gq-so.
*.
*/.
/* Тест CI: ge-aq-so */ // Метод «де».
EncounterCharacter еС1М = new EncounterCharacterC «CharForTestCl" ): eClM.adjustQuality(QUAL_STRENGTH, 40.Of ): // aq.
TestExecution.printReportToFile( outM,.
«Class test де-aq-so». eClM.sumOfQualitiesO, 100.Of ): // so.
Листинг 8.3 (продолжение).
/* Тест С2: ge-aq-aq-gq-so */.
EncounterCharacter еС2М = new EncounterCharacter( «CharForTestC2" );//ge eC2M.adjustQuality(QUAL_STRENGTH. 40.Of ); // aq.
eC2M.adjustQua1ity(QUAL_STAMINA, 20.9876f ); // aq.
TestExecution.printReportToFi1e( outM,.
«Class test ge-aq-aq-gq-so: part 1".
eC2M.getQualityValue( QUAL_STAMINA ). 20.9876f ); // gq.
TestExecuti on.pri ntReportToFi1e( outM.
«Class test ge-aq-aq-gq-so: part 2". eC2M.sum0fQualities(), 100.Of ); // so.
/* ТЕСТИРОВАНИЕ ИНВАРИАНТОВ.
* Проверить инвариант «qualValueI[i] >
=0".
* после выполнения последовательностей методов, приведенных выше */.
boolean truthM = true;.
for( int i =0: i
< qualityTypeS.length; ++i ) { /* Присвоить truthM значение false.
* если хоть одно вхождение eClM.qualValuel
< 0 */.
truthM = truthM && ( eClM.qualValueI[i] >
= O.Of ):.
}.
TestExecuti on.printReportToFi1e( outM.
«Class test for the invariant qualValuel[ i] >=0 ». truthM, true );.
/* Завершение */ outM.c!ose();.
System.out.println( «пТестирование класса EncounterChar завершено.» ); } // Конец testEncouterCharacterClass.
/** Тестирование всех методов класса одновременно.
* @рагат destinationP Место записи результатов тестирования.
* (Pexception IOException В случае невозможности открытия или.
* доступа к destinationP */.
public static void testEncounterCharacterMethods( String destinationP ).
throws IOException { /* Подготовка к тестированию */.
FileWriter outM = new Fi 1 eWnter( new File( destinationP ) ); System.out.println( «EncounterCharacter method test results on » + destinationP + «п» );.
/* Тестирование getEncounterCharacter() */ // На входе корректное имя.
EncounterCharacter eCNorM = new EncounterCharacter( «qwerty» ); TestExecution.reportToFi1e( outM.
«GetCharacter Test 1: nominal value». eCNorM.getName(), «qwerty» ):.
// На входе null.
EncounterCharacter eCNullM = new EncounterCharacter( null ); TestExecution.reportToFile( outM, «GetCharacter Test 2: null parameter», eCNullM.getNameO. GameCharacter.DEFAULTJAME );.
// Слишком длинное имя String tooLongM =.
«1234567890123456789012345678901234567890"; EncounterCharacter eCTooLongM = new EncounterCharacter(tooLongM); TestExecuti on.reportToFi1e( outM,.
«GetCharacter Test 3: Limit parameter values. ».
+ «max name len = » + eCTooLongM .maxNumCharsInNameO. eCTooLongM.getName(),.
tooLongM.substrings, eCTooLongM.maxNumCharsInName()) ); // Имя нулевой длины.
EncounterCharacter eCZeroM = new EncounterCharacter( ); TestExecuti on.reportToFi1e( outM.
«GetCharacter Test 4: zero-length».
eCZeroM .getNameO, GameCharacter.DEFAULTJAME );.
// Недопустимые символы.
EncounterCharacter eCPuncM = new EncounterCharacter( «а+Ь» ): TestExecuti on.reportToFi1e( outM,.
«GetCharacter Test 5: bad char + ».
eCPuncM .getNameO. GameCharacter.DEFAULTJAME );.
/* Проверка indexOfO на правильность имен всех характеристик */ for( int i = 0: i
< qualityTypeS.length; ++i ) try { TestExecution.reportToFile( outM,.
«indexOfO Test 1.» + i + «: valid name: » + qualityTypeS[i], indexOf(qualityTypeS[i]), i ); } catch( Exception eP ) { TestExecution.reportToFile( outM, «indexOfO Test 1: valid name: compare ». «indexOf(» + qualityTypeS[i] + «)», «with expected » + i );.
Листинг 8.3 (продолжение).
/* Проверка indexOfO на неправильное имя характеристики */ try { TestExecution.reportToFi1e( outM,.
«indexOfO Test 2: invalid name: zorch», indexOf(«zorch»), -1 ); } catch( Exception eP ) { TestExecution.reportToFile( outM, «indexOfO Test 2: valid name: compare », «indexOf(»zorch")". «with expected -1" );.
}.
/* Тестирование setQualityO */ // Подготовка к тестированию.
EncounterCharacter hank = new EncounterCharacter( «Hank» );.
// Номинальное значение.
hank.setQuality(QUAL_STRENGTH . 10.3f ):.
TestExecuti on.reportToFi1e( outM,.
«setQualityO Test 1: nominal value», hank.getQualityValue( QUALJTRENGTH ), 10.3f ):.
// Значение вне диапазона.
hank.setQuality( QUAL_PATIENCE. -6.2f ):.
TestExecuti on.reportToFi1e( outM,.
«setQualityO Test 2: nominal value»,.
hank.getQua1ityVa1ue(QUAL_PATIENCE ), O.Of );.
// Значение ниже минимального разрешенного.
hank.setQuality( QUAL_STAMINA, getTolerance() * 0,9f ):.
TestExecuti on.reportToFi1e( outM,.
«setQualityO Test 3: value close to zero», hank.getQualityValue(QUAL_STAMINA), O.Of ):.
// Тестирование adjustQualityO.
// Подготовка к тестированию и проверка: // все значения характеристик должны быть равны 20. EncounterCharacter harvey = new EncounterCharacter( «Harvey» ); TestExecuti on.reportToFi1e( outM,.
«adjustQualityO test 0: verify that values add to 100", harvey.sumOfQualitiesO, 100.Of ):.
// Номинальная установка:.
// сила = 30. остальные характеристики по 70/4 каждая harvey.adjustQuality(QUALJTRENGTH , 30.Of ): TestExecuti on.reportToFi1e( outM,.
«adjustQuality() test 1: values sum to 100 after adjusting», harvey.sumOfQualities(), 100.Of ); TestExecution.reportToFile ( outM,.
«adjustQuality() test 2: values adjusted as commanded», harvey.getQualityValue(QUAL_STRENGTH ). 30.Of );.
// Установка, приводящая к нулевому значению harvey.adjustQuality( QUAL_STAMINA, 99.Of ): TestExecuti on.reportToFi1e( outM.
«adjustQualityO test 3: verify low value reverts to zero», harvey.getQualityValue( QUAL_STRENGTH ). O.Of );.
// Завершение outM.close();.
System.out.println( « Method tests of EncounterCharacter concluded.» );.
}.
/** Класс для тестирования перерисовки персонажей.
* Создает окно, содержащее несколько копий изображения персонажа.
*/.
private static class testCharacterlmage extends Frame {.
/** Атрибут объекта, запоминающий, какое изображение персонажа выводить*/ private EncounterCharacter characterl;.
/** Основной конструктор: создает окно для тестирования некоторого Изображения персонажа.
* (Pparam characterP Персонаж, чье изображение должно тестироваться */.
testCharacterlmageCEncounterCharacter characterP) {.
super(characterP.getNameO); //Обычная инициализация characterl = characterP; //Запомнить, какой персонаж тестируем.
}.
/** Перерисовывает область изображения во фрейме.
*@param drawP Графический контекст для рисования персонажа.
*/.
public void paintCGraphics drawP) {.
Dimension frameSizeM = getSizeO: //Размер области окна.
int widthUnitM = frameSizeM.width / 5; //Удобное разбиение окна.
int heightllnitM = frameSizeM.height / 5; ,.
ппооолл.
Листинг 8.3 (продолжение).
//Нарисовать маленьким, лицом направо characterl.showCharacter(thi s, drawP,.
new Point(widthUnitM, heightllnitM). heightUnitM, false);.
//Нарисовать большим, лицом налево characterl.showCharacter(this, drawP,.
new Point(widthUnitM*4. heightUnitM*3), heightUnitM*2. true);.
//Нарисовать большим, лицом направо characterl.showCharacter(this, drawP,.
new Point(widthUnitM*2, heightUnitM*2), heightUnitM*2. false):.
//Нарисовать маленьким, лицом налево characterl.showCharacter(this, drawP,.
new Point(widthUnitM*3, heightUnitMM), heightUnitM, true);.
}.
} // Конец testCharacterlmage
Содержание этой главы в контексте процесса разработки программного обеспечения показано на рис. 9.1.
♦ Разделы 9.1-9.8.
♦ Упражнения.
♦ Примеры.
1) Пример 1. План управления конфигурациями программного обеспечения (SCMP). Приложение А: План создания базиса интеграции.
2) Документация по тестированию программного обеспечения (STD) для видеоигры Встреча
Рис. 9.1. Схема разработки программного обеспечения: темы главы 9
Вопросы, рассматриваемые в этой главе:.
♦ Планирование интеграции модулей системы.
♦ Понимание необходимых типов тестирования.
♦ Планирование и выполнение тестирования. На уровне всей системы в целом.
9.1. Введение.
9.1.1. Значение фазы интеграции.
Поскольку программные продукты довольно сложны по своей структуре, их формируют из частей, которые создаются независимо, а затем собираются в единое целое. Интеграция как раз и относится к процессу сборки. Различные виды тестов проводятся как над частично собранным приложением, так и над всем продуктом в целом.
Фаза интеграции водопадного процесса часто преподносит неприятные сюрпризы, связанные с несовместимостью интегрируемых частей. По этой причине USDP, в частности, старается ибежать сборки большого количества элементов, благодаря последовательной интеграции с помощью многочисленных итераций. Закрашенные области на рис. 9.2 показывают, что интеграция на самом деле имеет место и на итерациях конструирования и перехода [64].
При переходе от одной стадии процесса разработки к другой достаточно вероятна потеря информации. Отмеченные Майерсом [82] места в водопадном
Рис. 9.2. USDP для интеграции и тестирования
Рис. 9.3. Схема разработки
Во избежание этих потенциально возможных потерь информации используется непрекращающееся тестирование и интеграция. Несмотря на то что тестирование модулей как части целого приложения в конечном окружении имеет огромное значение (этому виду тестирования посвящена данная глава), оно не заменяет тщательного тестирования каждого модуля в отдельности перед добавлением его к системе (см. главу 8).
9.1.2. Верификация, валидация и системное тестирование.
Вспомните, что верификация позволяет определить, правильно ли мы создаем приложение. Другими словами, действительно ли мы на текущей фазе создаем именно те артефакты, что были специфицированы на предыдущей фазе? Применительно к интеграции верификация приравнивается к подтверждению того, что мы собираем именно те компоненты, которые мы планировали собрать, и именно тем способом, каким это было запланировано. Такая проверка может быть произведена при помощи инспектирования результатов интеграции.
Валидация позволяет выяснить, правильный ли результат у нас получается. Другими словами, удовлетворяет ли наш продукт требованиям, изложенным в SRS? На фазе интеграции этого добиваются с помощью системного тестирования.
процессе, в которых обычно происходят потеря информации и утрата понимания, показаны на рис. 9.3.
После завершения сборки, итерации или всего приложения тщательное тестирование требует, чтобы мы сначала выполнили модульные тесты функций (методов) и модулей (классов или пакетов). В этот раз, однако, эти тесты следует пройти в некотором контексте, а не изолированно друг от друга. Здесь требуется меньше драйверов и заглушек, что приводит к меньшему количеству сложностей и ошибок. Если мы тестируем финальную сборку, то нам вообще не следует использовать драйверы или заглушки. Вся разница между автономными модульными тестами и модульными тестами, выполняемыми в контексте системы, показана на рис. 9.4, где модулями считаются функции.
Рис. 9.4. Тестирование модулей в контексте
Например, модульное тестирование метода adjustQual ity() в классе СредаВстречи выполнялось с использованием тестового метода testEncounterCharacterMethodsO, добавленного в класс СредаВстречи (см. пример в главе 8). С другой стороны, тестирование adjustQual ityO в контексте готовой программы выполняется путем запуска программы таким образом, чтобы убедиться, что метод неоднократно вызывался. Это можно сделать путем периодического открытия окна во время игры и изменения значений характеристик.
Движение артефактов (преимущественно документов и кода) между стадиями проекта и между тестами разных типов [82] показано на рис. 9.5. Модульные тесты и тесты функций выполняются в двух разных режимах. В первый раз они выполняются изолированно, как модульные тесты. Второй раз они выполняются в контексте всей программы. По этой причине они пронумерованы дважды.
Связь запускаемых тестов с документацией показана на рис. 9.6. Вспомните, что валидация — это процесс, в результате которого мы хотим убедиться, что мы создаем «правильную» программу, и поэтому такие тесты проводятся согласно исходным требованиям. Другие тесты проверяют, что программа создается так, как мы намеревались, что является процессом верификации. Например, тесты интерфейса проверяют, точно ли реализация отражает запланированные интерфейсы.
Упомянутые на рис. 9.6 тесты будут кратко описаны далее, а затем тщательно разработаны в последующих разделах главы.
Рис. 9.6. Тестирование для верификации и валидации
Когда код системы интегрирован или частично интегрирован (нижняя часть рис. 9.5), становится возможным протестировать части в контексте всей системы вместо использования автономного подхода. Чтобы сфокусировать тестирование на разработанных частях программы, нам придется продумать подходящие входные данные.
1.
Нам пришлось создать заглушки и драйверы для выполнения модульного тестирования функций и классов, учитывая возможность существования ошибок и неполного охвата. Если их нельзя оставить в коде по организационным причинам или в связи с ограниченностью ресурсов, этот код можно отложить в сторону с возможным использованием в будущем. Альтернатива заключается в добавлении или исключении тестового кода посредством условной компиляции (присоединить или исключить код модульного тестирования).
2.
Аналогичным образом становится возможным повторно протестировать другие модули (например, пакеты) в контексте системы.
3.
Тестирование интерфейсов подразумевает повторную валидацию интерфейсов между модулями.
4.
Цель регрессионного тестирования заключается в проверке того, что добавления к системе не уменьшили ее возможностей. Другими словами, регрессионное тестирование проводится согласно требованиям, которые уже были выполнены перед добавлением новых возможностей. Только когда артефакт прошел регрессионное тестирование, мы будем готовы тестировать работу добавленного кода.
5.
Интегральное тестирование выполняется над частично сконструированной системой для проверки того, что результат интеграции дополнительных программ (например, классов) работает, как запланировано. Например, мы можем реализовать сначала пакет СредаВстречи и тщательно его протестировать. Затем мы можем реализовать пакет РолиВстречи. После интеграции этого пакета в пакет СредаВстречи мы выполняем интегральное тестирование, проверяя, что персонажи должным образом ведут себя в зонах.
6.
Системное тестирование выполняется над программой в целом или над разработанными версиями.
Системные и интегральные тесты проводятся в соответствии с архитектурой. Другими словами, они проверяют, чтобы программа следовала разработанной архитектуре и чтобы архитектура работала должным образом. Например, архитектура видеоигры Встреча разработана таким образом, чтобы при вступлении внешнего персонажа в зону, в которой находится персонаж игрока, генерировалось событие в пакете СхемаВстречи. Событие обрабатывается пакетом ИграВстреча. В некоторых случаях внешний персонаж может исчезать, что должно найти отражение в пакете РолиВстречи. Системные тесты проверяют такое поведение.
Системное тестирование также валидирует требования, как функциональные, так и нефункциональные. Нефункциональные требования включают в себя требования к рабочим характеристикам, таким как скорость работы и использование ресурсов.
7.
Тестирование удобства и простоты использования валидирует приемлемость программы для ее конечных пользователей.
8.
Тестирование инсталляции выполняется при установке программы па целевых платформах.
9.
Приемосдаточные тесты выполняются клиентом для валидации приемлемости программы.
Далее мы подведем итоги и обсудим типы тестирования более подробно.
9.2. Процесс интеграции 9.2.1. Описание интеграции.
Возможный процесс интеграции для первой итерации постройки подвесного моста (одноуровневая версия), а также для второй итерации (двухуровневая версия) приведены на рис. 9.7. Каждая итерация связана с этапом постройки. Спланирована точная последовательность действий по созданию сборок, которая завершает итерацию.
Рис. 9.7. Процесс создания сборок по итерациям
Простейший вид интеграции состоит из добавления новых элементов к базису (существующему коду) на каждой итерации по спирали (рис. 9.8). Фаза реализации состоит из кодирования новых частей, после которого эти новые части интегрируются в базис.
Рис. 9.8. Интеграция в спиральной разработке
Процесс интеграции кода требует не меньшего искусства и навыков, чем процесс интеграции физических объектов. Он может быть очень сложным. Как и в нашем примере с мостом, каждая программная итерация разбивается на стадии. Для USDP [64] это разбиение представлено на рис. 9.9.
На рис. 9.9 показаны группы итераций (например, итерации конструирования), где каждая итерация разбита на несколько сборок. Такая организация существенно важна для крупных проектов.
По завершении разработки архитектуры важно определить легкость, с которой части будут интегрироваться в проект. В отличие от некоторых физических разработок, в нашем случае редко удается завершить отдельные программные модули до их интеграции в проект. Одна причина этого заключается в том, что типовые программные модули выполняют обслуживание нескольких клиентов, в то время как физические модули обслуживают очень ограниченное число «клиентов», а зачастую вообще всего один. Например, каждая опора моста поддерживает лишь одну или две секции дороги. Кроме того, когда программные требования более понятны, становятся очевидны и новые клиенты для каждого модуля. Таким образом, программные сборки часто бывает необходимо интегрировать в частично сконструированные модули, как в «типовой» последовательности, а не как в «модульно-ориентированной» (рис. 9.10).
Рис. 9.9. Связь между сборками и итерациями в USDP
Рис. 9.10. Последовательности сборок: модульно-ориентированная и типовая
Хотя типовой процесс сборки имеет недостаток, заключающийся в работе с незавершенными модулями, он имеет и преимущество, состоящее в выполнении интеграции на ранних стадиях процесса разработки. Это помогает уменьшить риск, связанный с интеграцией завершенных крупных модулей.
Важность разработки проекта модулей возрастает благодаря сложности интеграции программ (таких, как классы и пакеты). Они должны быть как можно лучше сосредоточены на своей цели, а их взаимные интерфейсы должны быть как можно более узкими. Эти вопросы, касающиеся сцепления и связности, подробно обсуждались в разделе 5.1.4.
ОДИН ИЗ СПОСОБОВ ПЛАНИРОВАНИЯ ИНТЕГРАЦИИ И СБОРОК -.
1.
Поймите декомпозицию архитектуры.
♦ Постарайтесь сделать архитектуру простой для интеграции.
2.
Определите части архитектуры, которые будут реапизовываться на каждой итерации:.
♦ сначала соберите каркасные классы или делайте это параллельно;.
+ по возможности интегрируйте «последовательно»;.
♦ соберите достаточное количество пользовательских интерфейсов для привязки тестирования;.
+ документируйте требования для каждой итерации;.
♦ постарайтесь выполнять сборки снизу вверх хотя бы в течение некоторого промежутка времени, чтобы части системы были доступны, когда понадобятся;.
♦ старайтесь планировать итерации, чтобы уменьшить риск, в первую очередь уделив внимание самому крупному риску;.
♦ определите итерации и сборки, чтобы каждый вариант использования обрабатывался отдельно.
3.
Разложите каждую итерацию на сборки, если нужно.
4.
Спланируйте процессы тестирования, пересмотра и инспектирования.
5.
Обновляйте расписание проекта, отображая в нем результаты.
Указанные этапы будут обсуждаться более подробно далее в тексте этой главы.
Тестирование упрощается после объединения всех реализаций вариантов использования в каждой сборке вместо тестирования частей вариантов использования. Разрабатывая относительно небольшие варианты использования, вы, прежде всего, упрощаете процесс добавления их в сборку. Поскольку пользовательские интерфейсы рано или поздно все равно придется тестировать, желательно, чтобы их самих или их ключевые части можно было встроить как можно раньше, тем самым обеспечив возможность тестирования развивающейся программы. Альтернативой является сборка временных интерфейсов для использования во время интегрального тестирования.
Якобсон [63] отметил, что в общем случае в качестве руководства к планированию сборок проще использовать развитие сборок снизу вверх. Согласно этому подходу, части создаются перед их использованием для конструирования более крупных модулей. Восходящий процесс можно успешно скомбинировать с реализацией классов структур, которая является нисходящим процессом.
9.2.2. Типичная схема процессов интегрального и системного тестирования.
Типичная последовательность действий для интеграции программной системы включает следующие шаги:.
1.
Определить границы всех тестов.
2.
Для каждой итерации...
1) Для каждой сборки...
• Выполнить регрессионное тестирование для предыдущей сборки.
• В случае необходимости повторно протестировать функции.
• В случае необходимости повторно протестировать модули.
• В случае необходимости повторно протестировать интерфейсы.
Выполнить интегральное тестирование сборки (раздел 9.3.7).
Разработка итерации завершена.
2) Выполнить для итерации системные тесты и тесты удобства и простоты использования (разделы 9.3.4, 9.3.5).
Система реализована.
3.
Выполнить тестирование инсталляции (раздел 9.3.8).
Система установлена.
4.
Выполнить приемосдаточное тестирование (раздел 9.3.7).
Работа завершена.
Пакеты, выбранные для видеоигры Встреча, а также выбранные классы предметной области показаны на рис. 9.11. Интеграция этих пакетов состоит из их сборки по этапам, в результате которой должна получиться завершенная программа. Это выполняется в соответствии с планом интеграции, который можно документировать в SCMP. Это пример необходимости постоянного обновления документов. Вспомните, что SCMP является первым документом проекта. Мы возвращаемся к нему (в SDD) после определения архитектуры для определения последовательности интеграции.
Существенными факторами, определяющими последовательность интеграции, являются следующие:.
♦ Технические факторы:.
♦ использование модулей другими модулями. Собирайте и интегрируйте используемые модули до модулей, использующих их;.
♦ определение и использование классов каркаса.
♦ Уменьшение риска:.
♦ раннее выполнение интеграции;.
♦ как можно более раннее выполнение частей программы, порождающих основные риски.
♦ Требования:.
♦ показ частей или прототипов заказчикам.
Уровень каркаса
Рис. 9.11. Пакеты ролевой видеоигры и игры Встреча с отмеченными классами предметной области
Их порядок зависит от требований проекта. В рискованных проектах мы будем стремиться интегрировать рискованные части как можно скорее, чтобы оценить эффективность нашего проектирования. Показ отдельных частей программы заказчику также диктует нам порядок интеграции. В противном случае мы будем интегрировать использованные модули до модулей, использующих их, тем самым сводя к минимуму использование временного кода драйвера.
Поскольку пакет ИграВстреча в видеоигре Встреча использует (ссылается на) пакеты СредаВстречи и ПерсонажиВстречи, мы в первую очередь интегрируем последние два пакета. После этого игровой драйвер будет интегрирован, что позволит выполнить игру. Результирующий план интеграции показан на рис. 9.12.
Поскольку у нас есть только три программных пакета, которые мы должны интегрировать в игру, и поскольку наша игра является лишь прототипом, план интеграции довольно прост. Он состоит из двух итераций, разбитых на три сборки. Поскольку то, что мы создадим, будет лишь началом настоящей видеоигры, мы, возможно, захотим описать план интеграции в терминах USDP. Итак, план на рис. 9.12 показывает начальную итерацию, состоящую из двух сборок. Дальнейшие итерации пока еще не показаны.
Рис. 9.12. План интеграции для видеоигры Встреча
Альтернативный план интеграции для видеоигры Встреча показан на рис. 9.13. Этот план осуществляется поэтапно по классам из различных пакетов. Он не имеет целью закончить разработку пакета и затем интегрировать его с другими изолированно разработанными пакетами. Вместо этого в каждой сборке добавляются классы к нескольким пакетам. Достоинство этого плана интеграции заключается в том, что он интегрирует классы по ходу работы, стараясь избежать катастрофических ошибок, которые часто случаются при интеграции «готовых» пакетов.
9.3. Процесс тестирования 9.3.1. Интегральное тестирование.
Интегральное тестирование проверяет каждую сборку интеграции и каждую итерацию. Способ, которым можно спланировать и выполнить тестирование интеграции наряду с регрессионным и модульным тестированием, показан далее. Эти концепции объясняются позднее в этой главе.
Рис. 9.13. Альтернативный процесс создания сборки для видеоигры Встреча
ОДИН ИЗ СПОСОБОВ ПЛАНИРОВАНИЯ И ВЫПОЛНЕНИЯ -.
ИНТЕГРАЛЬНЫХ ТЕСТОВ.
1.
Решите, как и где хранить, повторно использовать и кодировать интегральные тесты.
♦ Отразите это в расписании проекта.
2.
Выполните столько модульных тестов (снова), сколько позволит время:.
♦ на этот раз в контексте сборки.
При этом не требуются драйверы или заглушки;.
♦ расставьте приоритеты тем модулям, в которых вероятнее всего могут появиться ошибки.
3.
Выполните регрессионные тесты.
Убедитесь, что существующие возможности системы не были нарушены.
4.
Убедитесь, что требования сборки определены должным образом.
5.
Выполните варианты использования, которые должны быть реализованы в сборке.
♦ Протестируйте на соответствие SRS.
6.
Выполните системные тесты для этой сборки.
Варианты использования являются идеальным источником тестовых вариантов для интегральных тестов. Как упоминалось выше, Якобсон [63] рекомендует согласовывать каждый вариант использования с одной сборкой. Идея в том, чтобы варианты использования строились на основе уже интегрированных частей, тем самым формируя представительные тесты использования программы (рис. 9.14).
Рис. 9.14. Связь между вариантами использования, итерациями и сборками
Для адекватной оценки программы требуется большое количество тестов, и необходимо использовать их методичную организацию. Один из стилей организации тестовых вариантов заключается в упаковке их в классы, специально созданные для тестирования. Например, тест пакета можно разработать как класс, принадлежащий пакету. Класс или, может быть, весь тестовый пакет можно посвятить тестированию целой программы. Это можно показать как значки артефактов тестирования на модели реализации, обсуждавшейся в разделе 7.1.4.
Обычно сборки состоят из кода нескольких разработчиков, поэтому возникает много проблем при интеграции кода для создания сборки. По этой причине мы стараемся начать интеграцию и интегральное тестирование на ранних этапах процесса разработки, что позволит выполнять код в его первичном контексте.
Интегральное тестирование выполняется во время разработки сборки. Такие тесты обычно состоят из регрессионных тестов с дополнительным тестированием для утверждения новых добавлений. Считается непрактичным постоянно запускать полные формальные интегральные тесты. Следовательно, часто на постоянной основе применяются масштабированные неформальные интегральные тесты: они утверждают только, что система вроде бы работает так, как должна. Иногда такие тесты называют «проверкой на дым». «Проверка на дым» либо убеждает программистов в том, что можно продолжать работать в том же духе (если не возникло никаких проблем), либо указывает на проблемы, которые могут привести к значительным задержкам на этапе интеграции.
План интеграции часто принимает форму, показанную на рис. 9.15 для примера банковской программы. По мере того как задача построения модулей подходит к концу, модули по очереди интегрируются в основу (например, сливаются с основным продуктом). В этом случае процесс интеграции происходит между неделями 23 и 41.
Рис. 9.15. Финальный план сборки кода и интеграции: пример банковского приложения
Процесс компиляции и тестирования частичных сборок нередко выполняется за ночь, и на время компиляции и тестирования разработка замораживается (рис. 9.16).
Рис. 9.16. Типичный процесс интеграции кода по дням
По мере достижения сроков выхода версии частота регрессионных тестов возрастает до тех пор, пока они не будут выполняться ежедневно, обычно ночью (см. рис. 9.16). Если регрессионное тестирование показывает, что существовавшая функциональность все еще имеет место, интегрированный код становится частью основы системы. Но если регрессионные тесты показали, что добавленный код порождает ошибки в существовавшей ранее функциональности, можно принять решение вернуться к базе ранее написанного кода, существовавшего до интеграции нового материала (эффективная «дезинтеграция»). Такой вид планирования ежедневных интегральных и регрессионных тестов был предложен в [21] и использовался, например, фирмой Microsoft.
9.3.2.
Сотрудники, участвующие в тестировании, и артефакты.
В этом разделе мы рассмотрим артефакты, связанные с процессом интегрального тестирования, согласно USDP. Они включают в себя следующее.
♦ Модель вариантов использования — набор вариантов использования, описывающих типичное использование программы и диаграммы последовательности, подробно описывающие их.
+ Тестовые варианты — входные данные для каждого теста.
♦ Процедуры тестирования — способ, которым следует создавать и проводить тесты и оценивать результаты. Это могут быть процедуры с ручным управлением либо использующие инструменты автоматизации тестирования.
4- Оценка тестов — подведение итогов, подробности и выгода от найденных ошибок.
♦ План тестирования — общий план руководства тестированием, в том числе порядок тестирования.
♦ Компоненты теста — исходный код самих тестов и программный код, который следует протестировать.
♦ Дефекты: отчет об обнаруженных в результате процесса дефектах, классифицированных по серьезности и типу.
Процесс интегрального тестирования в USDP требует работы таких специалистов, как тестовый инженер, разработчик компонентов, тестер интеграции и системный тестер. Их обязанности [64] показаны на рис. 9.17. Большинство артефактов, показанных на рис. 9.17, учитываются в IEEE-стандарте документации по тестированию, обсуждаемом далее.
9.3.3.
Тестирование интерфейсов.
Многие ошибки в программах связаны с проблемами интерфейсов между компонентами. При беспорядочном исполнении проекта группы находят более простым общение друг с другом, а не группы с группой. Вследствие этого проект и программные интерфейсы с легкостью могут быть поняты неправильно. Раздел определения интерфейсов в SDD является «Библией» для внутренних интерфейсов
программного приложения. Когда модули разработаны, можно приступать к тестированию интерфейсов. Оно осуществляется посредством генерации трафика через каждый интерфейс, обычно в форме функциональных вызовов.
Рис. 9.17. Артефакты и роли для интегрального тестирования
Например, представьте интерфейсы видеоигры Встреча (рис. 9.18). Для того чтобы убедиться в корректной работе интерфейсов, мы можем вызывать методы интерфейса последовательно в различных комбинациях. Последовательности, которые мы выбираем, должны выполнять многие комбинации методов интерфейса, но должны также быть осмысленны в контексте игры. В противном случае результат может быть непредсказуемым. Пример понятной последовательности вызовов для видеоигры, результат которых можно наблюдать на мониторе, показан на рис. 9.19. Последовательность методов, вызванных из mainO в объекте InterfaceTest:.
1.
//Разместить персонаж игрока в зоне «двор» с характеристиками //по умолчанию...
//Получить объект (интерфейс) EncounterCast playerChar = EncounterCast.getThePlayerCharacterO: //Установить тест зоны «подвал» EncounterGame.setLocation( playerChar, «courtyard» );.
2.
//Установить состояние игры Ожидание.
EncounterGame.setState( «waiting» );.
3.
//Теперь проверить состояние игры.
state = EncounterGame.getStateO;.
4.
//Вывести название состояния.
state.pri ntStateName();.
5.
//Предположим, что внешний персонаж Фредди в зоне «кухня».
foreignChar = EncounterCast.getTheForegnCharacter(): EncounterLayout.setLocation( foreignChar. «kitchen» );.
6.
//Предположим, внешний персонаж Фредди входит в зону «двор».
EncounterLayout.setLocation( foreignChar, «courtyard» );.
7.
//Проверить, что текущее состояние - Контакт.
state = EncounterGame.getState(): state.pri ntStateName();.
//Предположим, персонаж игрока убежал в зону «подвал» EncounterLayout.setLocation( foreignChar. «dungeon» );.
8.
//Проверить, изменились ли характеристики игрока.
//должным образом
Рис. 9.18. Тестирование интерфейсов для Встречи
Рис. 9.19. Сценарий теста интерфейсов для игры Встреча
9.3.4. Системное тестирование.
Системное тестирование является кульминационным моментом интегрального тестирования. Оно состоит из тестов «черного ящика», утверждающих согласованность всей программы с программными требованиями. По мере возможности системные тесты выполняются при запущенной программе в требуемой среде. Иногда, однако, нам приходится довольствоваться лишь запуском системных тестов в среде или конфигурации, отличных от имеющихся у заказчика. Например, мы не будем считать необходимым тестировать апплеты на каждом типе персональных компьютеров. С другой стороны, апплеты следует протестировать на всех основных версиях всех широко распространенных браузеров.
Поскольку системные тесты гарантируют удовлетворение требований, эти тесты должны систематически подтверждать каждое требование. Для принудительной демонстрации выполнения каждого требования потребуется значительный объем тестового кода. На этом этапе мы также должны проверить все варианты использования.
USDP предпринимает попытку организовать большинство требований по вариантам использования. Тестирование в этом случае проще, чем тестирование отдельных атомарных требований.
Основные свойства, которые подвергаются проверке при системном тестировании [74], перечислены ниже.
♦ Объем.
Тестируемый продукт исследуется при подаче больших объемов входных данных.
♦ Удобство и простота использования.
Требуется измерить реакцию пользователя (например, по шкале 1-10).
+ Производительность.
Измерьте скорость в разных условиях.
4- Конфигурация.
Например, сконфигурируйте систему под разные аппаратные и программные архитектуры и измерьте время настройки.
♦ Совместимость с другими обозначенными программными приложениями. Измерьте время адаптации.
♦ Надежность и доступность.
Измерьте период работоспособного состояния в течение длительного времени.
♦ Безопасность.
Подвергните систему попыткам несанкционированного доступа. Измерьте среднее время взлома.
♦ Использование ресурсов.
Измерьте степень использования оперативной памяти, жесткого диска и т. д.
+ Установка.
Установите программу в разных условиях и измерьте время установки.
•♦■ Восстанавливаемость.
Выполните принудительные действия для зависания программы. Измерьте время восстановления.
♦ Удобство в эксплуатации.
Выполните обслуживание программы в разных ситуациях. Измерьте время обслуживания.
♦ Загрузка и стресс.
Подвергните программу экстремальному трафику данных и событий.
Надежность и доступность измеряются такими метриками, как среднее время наработки на отказ (MTBF — Mean time between failure). Чтобы получить эту величину, сначала нужно сформулировать определение ошибки — например, «полное зависание программы». В действительности можно определить несколько уровней ошибок. Для вычисления среднего времени наработки на отказ тестер запускает программу, засекает время, а затем выполняет (в идеале) произвольный сценарий игры до тех пор, пока система не зависнет. Далее отмечается время и вычисляется промежуток времени. Этот процесс выполняется повторно несколько раз. Среднее время наработки на отказ — это среднее арифметическое полученных значений.
Термин удобство в эксплуатации относится к простоте или сложности, с которой можно поддерживать работу программы. Например, если экспертное системное приложение работает с собственной базой знаний, то она должна быть легко модифицируема.
9.3.5. Тестирование удобства и простоты использования.
Хороший интерфейс может значительно повысить ценность программы. Тестирование удобства и простоты использования утверждает приемлемость программы для пользователей.
9.3.5.1. Тестирование требований пользовательских интерфейсов.
Основная задача тестирования удобства и простоты использования заключается в гарантии того, что программа удовлетворяет своим требованиям. Вспомните, например, из раздела 3.3.5, что существует огромное количество типов окон, и что они могут появляться несколькими возможными способами. Вдобавок может потребоваться специальная синхронизация. Например, в видеоигре Встреча существует определенная задержка между вводом новых значений характеристик персонажа и моментом, начиная с которого новые значения вступают в силу.
Тестирование удобства и простоты использования содержит в себе валидацию этих требований.
Один из способов организации такого тестирования заключается в измерении степени удовлетворенности, полученной пользователями от применения программы.
9.3.5.2. Метрики удобства и простоты использования.
Критерии оценки удобства и простоты использования должны быть сформулированы заранее. Например, мы можем потребовать, чтобы произвольная группа из 30 пользователей нашей домашней финансовой программы оценила программу (табл. 9.1). Необходимое количество пользователей определяется статистически и зависит от размеров ожидаемой базы заказчика и желаемой вероятности ошибочного заключения.
Таблица 9.1. Пример значений метрик удобства и простоты использования
Метрика
Среднее значение (из 10)
Простота просмотра
8,5
Простота использования
8,5
На практике данные по удобству и простоте использования будут более подробными, чем указанные в табл. 9.1. Например, Кит [74] перечислил критерии, также важные для тестирования удобства и простоты использования.
♦ Доступность.
Насколько легко могут пользователи входить, ориентироваться и выходить из системы?.
Например, измерить среднее затраченное время на...
♦ Способность реагировать.
Насколько быстро программа позволяет пользователям достичь определенных целей?.
Например, измерить среднее затраченное время.
♦ Эффективность.
Насколько малы необходимые шаги для выбранной функциональности? «Малость» устанавливается в теории.
Например, также измерить минимальное время из пользовательского примера.
♦ Ясность.
Насколько легко понимают продукт, пользуются документацией и вызывают справку?.
Например, измерить время, затраченное на стандартный запрос. Вдобавок к этим характеристикам нам понадобятся метрики, специфичные для конкретной программы, например:.
Насколько легко для вас заполнить стандартную форму о случившемся ДТП (по десятибалльной шкале)?.
При разработке опросников удобства и простоты использования задача заключается в получении данных, позволяющих инженерам направить усилия на исправление наиболее серьезных упущений, не злоупотребляя временем и терпением пользователей. Накопление данных по удобству и простоте использования может оказаться дорогим процессом, поскольку пользователи часто ожидают компенсации за свое потраченное время и предоставление информации. Например, фирма — клиент автора [74] — разрабатывает программное обеспечение для устройства, используемого врачами. Фирма предоставляет врачам бесплатный ужин и сотни долларов только для того, чтобы они просмотрели и прокомментировали экранные снимки и демонстрации. Разработчик считает, что все эти затраты окупятся.
9.3.6.
Регрессионное тестирование.
Когда программа разрастается до больших размеров, повышается важность системного тестирования. Это особенно заметно, когда в большую программу вносятся изменения и разработчикам нужно утвердить тот факт, что изменение не повредило существующую функциональность. Первый вопрос, возникающий после интеграции изменения, обычно следующий: «Остался ли продукт тем же, чем он был раньше, с расширенной функциональностью?» Повторное полное инспектирование обычно нерационально, поэтому важным практическим методом получения ответа на этот вопрос является проверка того, что система продолжает проходить тот же определенный набор системных тестов, что и до изменений. Этот процесс верификации называется регрессионным тестированием.
Регрессионное тестирование проводится достаточно часто. Если время не позволяет выполнить регрессионное тестирование, выбираются тесты, которые система после внесения изменений с наибольшей вероятностью не пройдет.
9.3.7.
Приемосдаточное тестирование.
Разрабатывающая программу организация и организация-заказчик являются двумя сторонами, заключившими контракт. После завершения работ мудрый разработчик получает окончательное утверждение заказчика, согласно которому можно начинать поставку программы. Приемосдаточные тесты разрабатываются для убеждения клиента в том, что указанная программа действительно создана. Приемосдаточные тесты могут ничем не отличаться от системных тестов, созданных разработчиком, но на этот раз они должны быть официально засвидетельствованы организацией-заказчиком и проверены на целевых платформах.
От заказчиков часто требуется промежуточная частичная оплата на основе промежуточных версий. Это частичные реализации и проекты, которые также требуют проведения приемосдаточного тестирования.
9.3.8. Тестирование инсталляции.
Тот факт, что мы протестировали программу в нашей собственной среде, вовсе не означает, что программа будет корректно работать в среде заказчика, поскольку при переходе из одной среды в другую открываются необъятные просторы для новых ошибок. Тестирование инсталляции состоит из тестирования программы в целевой аппаратной конфигурации. Это влечет за собой инсталляцию программы в целевой среде и выполнение комплекта системных тестов. Для заархивированных прикладных программ тестирование инсталляции состоит из выполнения программы на платформах, представляющих собой типовые среды заказчиков.
9.4. Документирование интеграции и тестирования.
9.4.1. Стандарты документации по тестированию.
Стандарт ANSI/IEEE 829-1983 на Документацию по тестированию программного обеспечения (STD — Software Test Documentation), заново подтвержденный в 1991 году, состоит из следующих разделов:.
1.
Введение.
2.
План тестирования.
Тестируемые элементы, границы, подход, ресурсы, расписание, персонал.
3.
Проект тестирования.
Тестируемые элементы, подход, план в подробностях.
4.
Тестовые варианты.
Наборы входных данных и событий.
5.
Тестовые процедуры.
Шаги настройки и выполнения тестовых вариантов.
6.
Отчет о проведении тестирования элементов.
Тестируемый элемент, физическое местоположение результатов, ответственный за проведение тестов.
7.
Журнал испытаний.
Хронологическая запись, физическое местоположение теста, название теста.
8.
Отчет о происшествиях во время тестирования Документирование любого события, имевшего место во время тестирования, требующего дальнейших расследований.
9.
Итоговый отчет о тестировании Итог всего вышеперечисленного.
1.
В разделе Введение объясняется содержание тестов и их общие принципы. Например, если программа управляет оборудованием помещения скорой помощи, именно в этом разделе мы должны объяснить наш общий подход к тестированию моделей, сводящийся к тестированию в условиях данных помещений.
2.
План тестирования объясняет, как следует организовать персонал, программы и оборудование, чтобы выполнить тестирование. Например: «Временной модуль будет тестировать Джо на протяжении недель 30-33; модуль мониторинга сердца будет тестировать Сьюзан на протяжении недель 26-30; интеграцию этих двух модулей будет тестировать Эд на протяжении недель 31-33; ... ».
3.
Проект тестирования отражает следующий уровень детализации после плана тестирования. Он раскрывает значение соответствующих программных элементов, описывает порядок, в котором их следует тестировать, называет тестовые варианты, которые следует применить. Например: «Джо будет тестировать временной модуль отдельно на протяжении недель 30-33, используя тестовую процедуру 892 и драйвер 8910; Сьюзан будет тестировать модуль мониторинга сердца отдельно на протяжении недель 26-30, используя тестовую процедуру 555 и драйвер 3024; Эд будет тестировать сборку, интегрирующую эти два модуля (сборка 7), используя...».
4.
Тестовые варианты состоят из наборов входных данных и точных входных сигналов, которые должны использоваться для выполнения теста. Например, модуль мониторинга сердца должен работать по тестовому файлу 892, в котором содержатся конкретные данные по конкретному пациенту в конкретное время. Мы должны точно указать, где находится этот тестовый файл.
5.
Тестовые процедуры — это полные подробные шаги выполнения плана тестирования. Сюда входят все процедуры настройки, имена необходимых файлов с исходными данными и объектным кодом, выходные файлы, logфайлы, файлы с тестовыми вариантами и отчеты. Например, тестовая процедура 892 могла бы быть аналогична приведенной ниже:.
1) откомпилировать временной модуль вместе с драйвером 8910;.
2) установить путь ...;.
3) загрузить содержимое файла 672 в файл, названный input, в том же каталоге, что и объектный код;.
4) выполнить код со следующими параметрами...;.
5) в появившемся окне ввести Джонс в текстовом поле имени...;.
Причиной использования такого уровня детализации является тот факт, что когда тест выявляет дефект, важно знать точные обстоятельства, при которых этот дефект возник. Без подробной пошаговой документации тестирования такие тесты невозможно достоверно воспроизвести, и дефект, возможно, вообще не удастся выявить повторно. В этом случае ошибку исправить трудно, если не невозможно.
6.
Отчет о проведении тестирования элементов резюмирует запускаемые нами тесты, список ответственных лиц, используемые версии продукта и т. д.
7.
Журнал испытаний представляет собой подробный текущий отчет о полученной во время тестов информации. Он может оказаться полезен при попытке воспроизвести ситуации, в которых тест завершился неудачно.
8.
Отчет о происшествиях уточняет заслуживающие внимание события, происшедшие во время тестирования. Примерами могут быть отклонения от нормальной работы программы и допущенные в процессе тестирования ошибки.
Формат IEEE можно использовать для тестов большинства типов, упомянутых в этой главе. Этот формат использован в примере с видеоигрой Встреча в конце главы для построения интегральных тестов.
9.4.2. Организация документации по интеграции и тестированию.
Полезным документом, в котором описан процесс сборки частей программы, является документ управления конфигурациями (в терминах IEEE это SCMP) (рис. 9.20). Организация этого документа показывает, что описание SCMP содержится в SPMP, а также показывает список сотрудников, отвечающих за SCMP. Сам SCMP описывает конкретные процедуры для поддержки (хранения, маркирования, согласования и т. д.) различных версий различных документов, в том числе и SPMP. В нем также точно определено местоположение этих документов. Последняя спецификация разрастается, и ее предпочтительнее описать в приложении к SCMP. SCMP и его приложение должны ссылаться на документацию по тестированию (в терминах IEEE — STD) для четкого отслеживания выполняемых тестов, соответствующих тестовых вариантов, процедур, планов и т. д. и существующих версий кода, которые тестируются.
Связь между различными тестовыми документами и их связь с существующей документацией показана на рис. 9.21. STD-документация по тестированию содержит в себе все типы тестирования, описанные в этой главе. Она может также содержать модульное тестирование (в зависимости от степени документирования модульного тестирования). Стандарт IEEE для документации по тестированию приводится ранее в начале раздела 9.4.1; эта организация документации применяется к каждой сборке и к каждому из разнообразных типов тестов (рис. 9.21).
Артефакты различных тестов используются вторично, что показано на рис, 9.21 пунктирными линиями. Например, при тестировании сборки обычно используются планы тестирования, проекты, варианты и процедуры, разработанные для тестирования предыдущих сборок. Системное тестирование использует артефакты (тестовые варианты и т. д.), которые были разработаны для тестирования финальной сборки; приемосдаточные тесты используют артефакты системного тестирования, а тестирование инсталляции использует артефакты приемосдаточного тестирования.
Рис. 9.20. STD в контексте разработки
Рис. 9.21. Организация документации интегрального и системного тестирования
9.5. Итерации перехода.
После интеграции программного приложения необходимо выполнить некоторые действия, прежде чем выпускать его новую версию. Требуемые действия объединены Якобсоном в итерациях перехода (согласно USDP). Цели этих итераций представлены на рис. 9.22. На этом же рисунке подведен итог относительного количества требований, анализа и т. д., необходимых для этих итераций на стадии перехода.
Переход
• Найти дефекты через использование заказчиком
Требования
• Протестировать пользовательскую документацию и систему помощи
Анализ
• Определить реалистично, отвечает ли программное приложение требованиям заказчика
Проектирование
• Устранить риск, связанный с развертыванием приложений
Реализация
• Удовлетворить различные рыночные цели
Тестирование
■■■
Итер. #т+1
Итер. #к
Рис. 9.22. Цели итераций перехода
9.5.1. Альфа- и бета-версии.
Во многих случаях предполагаемые пользователи хотят наряду с заказчиками участвовать в процессе системного тестирования. Этот процесс управляется с помощью альфа- и бета-версий.
♦ Альфа-версия:.
♦ внутренние и высоконадежные пользователи;.
♦ множественное тестирование;.
♦ позволяет получить предварительную оценку реакции пользователя;.
♦ выгодна разработчикам третьей стороны;.
♦ препятствует конкуренции.
♦ Бета-версия:.
♦ избранные заказчики;.
♦ множественное тестирование;.
♦ позволяет получить реакцию заказчика.
Альфа-версии даются внутренним пользователям или строго отобранной надежной группе внешних пользователей для раннего предвыпускного использования.
Назначение альфа-версий — предоставить организации-разработчику обратную связь и информацию о дефектах от группы людей, превосходящей тестеров в количестве, без изменения репутации пока не выпущенного продукта. После распространения альфа-версии выпускается бета-версия.
Бета-версии раздаются части сообщества заказчиков с учетом того, что заказчики должны будут докладывать об обнаруженных ошибках. Кроме того, альфаи бета-версии используются для убеждения потенциальных клиентов в том, что помимо обещаний разработчика существует уже почти готовый продукт. Иногда распространение предварительного выпуска продукта является стратегическим приемом, используемым для внушения покупателям мысли о том, что они не должны покупать конкурирующие продукты, а вместо этого следует подождать выхода программы, которая проходит стадию бета-тестирования. Сопутствующие этические вопросы не освещаются в этой книге.
Основной мотивацией альфа- и бета-тестирования является получение более полной информации о продукте. Разработчики могут получить информацию о программе (обычно о ее программном интерфейсе), чтобы в будущем иметь возможность начинать разработку программ, использующих эту. Пользователи получают возможность обдумать покупку этой программы.
9.5.2. План итераций перехода.
Основные этапы итераций перехода (финальных) показаны на рис. 9.23.
Рис. 9.23. План итераций перехода
Критерием остановки являются условия, при которых продукт следует допустить к приемосдаточному тестированию. Если эти условия не определены заранее, тестирование обычно проводят до тех пор, пока не закончится время: обычно это не самый эффективный способ использования времени тестирования. Примером критерия остановки может быть: «Максимум две ошибки среднего или низкого уровня найдено за неделю бета-тестирования». Приведем классификацию критериев остановки, предложенную в [74].
♦ Завершение конкретной методологии теста.
Завершить процедуры метода или инструмента.
♦ Оценка границ в процентах по каждой категории.
Предопределить процент каждой и показать, как его рассчитывать.
Например, «95 % утверждений».
♦ Скорость нахождения ошибок.
Предопределить скорость при заданном уровне важности.
Например, «2 дефекта средней важности или менее на каждые 100 часов.
работы».
♦ Общее число обнаруженных ошибок.
Вычислить (если возможно) из процента оставшихся ошибок.
Предопределить процент обнаруженных ошибок.
Например, «95 % оценки существующих ошибок найдено».
На рис. 9.24 упоминаются оставшиеся ошибки, но как мы можем оценить число оставшихся ошибок? Один из способов называется «засев». Он состоит из добавления некоторого количества ошибок в программу и определения их процентного соотношения среди ошибок, найденных независимым тестером за определенный срок. Это число затем используется для оценки числа оставшихся дефектов.
Например, если 3 из 50 посеянных ошибок найдены за период тестирования, можно оценить количество необнаруженных ошибок на каждую обнаруженную как 47/3 = 15,67. Таким образом, если за тот же период было найдено 100 непосеянных ошибок, в системе осталось порядка 100x15,67 = 1567 необнаруженных ошибок.
При использовании некоторых из этих критериев в проектах могут быть использованы графики для определения сроков выпуска продукта (рис. 9.24). В этом примере использовались три критерия остановки. Критерий остановки для скорости обнаружения ошибок гласит: «Не более семи ошибок найдено за каждые 1000 часов тестирования одной недели на протяжении хотя бы четырех последовательных недель». (Чтобы уместить 1000 часов тестирования в одну неделю, тестирование следует проводить параллельно на нескольких копиях одной программы.) Это банковская программа, и тестирование операций депозита и снятия проводится отдельно. В случае, изображенном на рисунке, последним критерием, который необходимо выполнить, является «Процент протестированных операций снятия», поэтому продукт не будет выпущен до устранения этого дефекта (неделя 26).
9.6. Качество в интеграции, верификации и валидации.
9.6.1. Качество, к которому следует стремиться.
Хорошие планы интеграции всегда тщательно продуманы, а эффективные системные и интегральные тесты подробны и всесторонни. Приведенные ниже метрики содействуют этим свойствам.
Рис. 9.24. Критерий остановки: графическое представление
9.6.2. Метрики интегрального и системного тестирования.
Ниже приведены метрики, взятые из IEEE 982.1-1998 — словаря стандартных метрик [59].
♦ IEEE 1. Плотность отказов = [Число уникальных отказов, найденных при тестировании] / [Число строк кода].
♦ IEEE 2. Плотность дефектов = [Число уникальных дефектов, найденных при тестировании] / [Число строк кода].
♦ IEEE 5. Функциональный охват теста = [Число протестированных функциональных требований] / [Общее число требований].
♦ IEEE 10. Индекс зрелости программы = [ М - F.
- F
- F
] / М, где ♦ М — число частей имеющегося базиса;.
+ F
— число добавленных частей в текущем базисе по сравнению с предыдущим базисом;.
♦ F
— число частей в текущем базисе, измененных по сравнению с предыдущим базисом;.
♦ F
— число частей, удаленных из предыдущего базиса.
«Частями» могут быть функции, классы, пакеты, модули и т. д. Развитые программы имеют индекс зрелости, близкий к единице. Это означает, что число затронутых частей невелико по сравнению с общим числом компонентов.
♦ IEEE 18. Надежность работы выражается вероятностью того, что в k произвольных случаях работы программа вернет корректный результат. Эта величина оценивается через выполнение некоторого числа запусков программы (N) и вычисления числа случаев успешной работы (S). Вероятность успеха, таким образом, вычисляется как S/N, а вероятность возможности отработать k раз успешно — как произведение вероятностей каждого успешного запуска, то есть [5/W] х [5/N] х ... х [5/W], или [S/Nk. Входные данные для каждого случая выбираются произвольно и независимо от предыдущего запуска.
♦ IEEE 20. Среднее время обнаружения k отказов. Это значение вычисляется аналогично надежности работы (см. IEEE 18 выше).
♦ IEEE 21. Уровень безупречности программы. Эта метрика оценивает отсутствие отказов в программе на операционной стадии [59].
♦ IEEE 22. Оценка числа оставшихся отказов (методом засева). Эта оценка получена путем «засеивания» в программу N произвольных отказов. Если s — число найденных засеянных отказов, а / — число других отказов, найденных за тот же период тестирования, оценка равна / х N / s.
♦ IEEE 24. Охват теста. Это число оценивает законченность выполненного тестирования (например, доля выполненной работы, умноженная на долю выполненных тестов). Вот формула для вычисления этой оценки:.
ОТ (в процентах) = [ [Число реализованных требований] / [Число требований] ] х [ [Число протестированных программных примитивов] / [Общее число примитивов в программе] ] х 100.
Программные примитивы — это тестируемые модули программы. Сюда относятся методы, классы и пакеты.
♦ IEEE 30. Средний период ошибки (MTTF — Mean-time-to-failure). Измеряется посредством запоминания промежутков времени между всеми парами замеченных последовательных ошибок и их усреднения. При измерении промежутков обычно используется фактическое истекшее время, а не время центрального процессора.
♦ IEEE 36. Точность теста. Этот тест оценивает надежность процесса тестирования и представляет собой побочный продукт описанного выше теста 22.
Точность теста = N
/N, где N — это число засеянных отказов, a N
— это число засеянных ошибок, найденных во время тестирования.
9.6.3.
Инспектирование системного и интегрального тестирования.
Несколько аспектов интеграции поддаются процессу инспектирования. Сюда относятся части SCMP, относящиеся к последовательности интеграции, и различные планы тестирования, например План интеграции (IP) и План тестирования программного обеспечения (STP). Примером дефекта в плане интеграции может быть отсутствие необходимого элемента (модуля, класса, метода и т. д.) на некоторой стадии интеграции, например, когда необходимо, чтобы элемент присутствовал для формирования тестируемой сборки или варианта использования. Примером дефекта в интегральном тесте является отсутствие тестового шага, являющегося частью соответствующего варианта использования.
Последовательность сборок и их тестов может быть очень сложной. Отсюда и вытекает выгода от инспектирования.
9.6.4.
Привлечение группы контроля качества к интегральному и системному тестированию.
Сотрудники группы контроля качества обычно более активно задействованы на этапах тестирования сборок и системного тестирования, чем на любом другом этапе процесса.
Для понимания важности и сложности процесса контроля качества рассмотрим пример разработки программы для составления прогноза погоды. Одна из основных функций этой программы заключается в преобразовании огромных объемов данных в графические изображения. Нужно учесть, что данные постоянно меняются, а также существует множество способов представления информации. Конкуренция и частые запросы служб прогноза об улучшении характеристик программы приводят к постоянным изменениям и улучшениям программы. Вдобавок производитель стремится выпускать новые, улучшенные версии для получения большей прибыли. В организации-разработчике такого типа обычно имеется группа контроля качества, тестирующая новые характеристики и проводящая регрессионное тестирование. Представьте себе перспективы производителя, если система изображения откажет на глазах у миллионов людей.
Одной важной проблемой группы контроля качества в этом случае является воспроизводимость. Группа контроля качества должна воспроизвести сеанс работы, в котором пользователь щелкнул мышью на каком-то поле в некоторый момент изображения грозы. Инструменты, записывающие и воспроизводящие действия пользователя, описываются далее в разделе 9.7. Они могут помочь, но не охватывают абсолютно все типы программ. Существует значительный объем дополнительной работы, которую необходимо выполнить для измерения и поддержания качества.
При обнаружении ошибок на системном уровне необходимо оповестить соответствующих сотрудников. Это может оказаться важным дипломатическим, управленческим и техническим заданием. Дипломатическим, потому что разработчики не любят слышать о своих ошибках; управленческим, потому что согласованные дефекты необходимо проследить, и ответственность за это ляжет на многих людей; техническим, так как определение причин ошибки может быть очень сложным.
9.6.5. Системная интеграция и модель СММ.
Время окончания проекта является удачным моментом для оценки использованного процесса и для организации улучшений процесса. Типичная организация стремится перейти на следующий уровень СММ.
1.
Начальный.
Неопределенный процесс, построенный конкретно для текущей цели.
2.
Повторяемый.
Отслеживаются фактические стоимость, план, функциональность.
3.
Установленный.
Документированный, стандартизированный настраиваемый процесс.
4.
Управляемый.
Подробные оценки, управление.
5.
Оптимизированный.
Последовательные дискретные улучшения процесса. В качестве примера представьте себе, что наша организация находится на уровне 3 и пытается достичь уровня 4. Таким образом, команде придется тщательно измерять и контролировать проект (а не позволять проекту управлять группой разработчиков). Подведение итогов работы может иметь форму, показанную в табл. 9.2.
Таблица 9.2. Пример подведения итогов: анализ требований через системную интеграцию
Набор метрик
Управляемость
Действия
Требования
Поддерживаются только 2 из 4 метрик требований
Хорошая — проблемой пренебрегли, времени хватает
Поручить инженеру группы контроля качества поддержку SRS и всех четырех метрик. Л. Д. к 01.03
Проект
Не смогли.
определить.
метрики
Низкая — справились за 140 %.
запланированного времени
X. Р. должен выбрать три лучшие метрики для программы такого типа; оценить стоимость и график выполнения; к 05.03. Л. Д. должен описать, как нам следует расставить приоритеты в работе по проекту; к 01.04. С. Т. должен решить, достаточно ли времени было выделено на разработку проекта или процесс был неэффективен (и сформулировать причины этого); к 15.03
Набор метрик
Управляемость
Действия
Реализация
Хорошая
Выполнена за 130 %.
запланированного времени
Просмотреть методы оценки числа строк кода. Определить три главные причины превышения. Д. А. к 05.03
Интеграция и выпуск версии
Использовалось слишком много бесполезных метрик
Адекватна
Удалить метрику «скорость подписи бета-узла». Заново определить эффективность других использовавшихся метрик. Б. В. к 10.03
9.7. Инструментальные средства интегрального и системного тестирования.
Для абсолютно полного тестирования обычно требуются автоматизированные инструменты тестирования. Якобсон и другие [64] предложили в лучшем случае автоматизировать по крайней мере 75 % тестов, а остальную часть тестов проводить вручную. Некоторые возможности инструментов тестирования перечислены ниже.
1. Запись и воспроизведение. Без возможности записывать и воспроизводить события мыши и клавиатуры качество тестирования падает, так как тестерам приходится выполнять это вручную. Это утомительно и дорого. Вдобавок результаты могут не быть в точности сравнимыми, поскольку люди не могут абсолютно точно повторять действия. Классификация инструментов записи и воспроизведения [74] показана ниже.
♦ Собственные / встроенные в программу.
Тестовые приложения, интегрированные в тестируемую программу. Могут скомпрометировать тестируемую программу. Наименее дорогие.
♦ Собственные / встроенные аппаратно.
Тестовая аппаратура, интегрированная с тестируемой программой. Может скомпрометировать тестируемую программу.
♦ Невстроенные.
Используют отдельную тестовую аппаратуру. Не компрометируют тестируемую программу. Наиболее дорогие.
Большинство распространенных инструментов записи-воспроизведения являются тестами, встроенными в программу. Примером невстроенных системных тестов являются тесты военной оперативной системы управления реального времени, в которой взаимодействие с пользователем эмулируется с помощью отдельных устройств, подключенных к тестируемой программе. Внешние устройства программируются так, чтобы они выдавали входные сигналы и программа не могла отличить эти сигналы от ввода данных реальным пользователем.
Инструменты записи-воспроизведения могут оказаться очень полезными, но они крайне чувствительны к изменениям в пользовательском интерфейсе. Небольшое изменение пользовательского интерфейса может свести на нет весь набор автоматически выполняемых тестов.
2.
Неоднократный запуск тестовых сценариев. Возможность автоматически выполнять тесты программы дает тестерам возможность не повторять один и тот же тест вручную с различными параметрами.
3.
Запись результатов тестов. Это освобождает тестеров от необходимости реализовывать эту функцию.
4.
Учет времени. Автоматические тестовые инструменты могут измерять и записывать истекшее время и загрузку центрального процессора.
5.
Запись ошибок в работе программы. Некоторые автоматические инструменты тестирования могут записывать ошибки, возникшие в ходе работы программы.
6.
Управление регрессионным тестированием. Вспомните, что регрессионное тестирование необходимо для утверждения того факта, что изменения предыдущей версии не добавили новых ошибок. Регрессионные тесты меняются во времени по мере реализации все больших возможностей. Некоторые автоматические инструменты тестирования могут вести учет этих тестов и применять их по требованию.
7.
Генерация тестовых отчетов. К автоматическим инструментам тестирования относятся генераторы тестовых отчетов, исключающие необходимость писать многочисленные отчеты о тестировании вручную либо создавать свой собственный инструмент генерации отчетов. Последнее было частично сделано для нашего примера в конце главы.
8.
Генерация тестовых данных. Среди наиболее полезных инструментов тестирования есть инструменты для генерации тестовых данных. Эти инструменты генерируют входные данные, удовлетворяющие большинству тестов «черного» и «белого ящика», обсуждавшихся в этой главе и в главе 8. Примером является генерация произвольных комбинаций входных данных. Некоторые тестовые инструменты также облегчают тестирование «серого ящика» и системные тесты, выполняющие проверку взаимодействия модулей. Не следует ожидать, что эти инструменты будут генерировать корректные выходные данные для каждого тестового варианта, поскольку эта возможность является целью создаваемой нами программы!.
9.
Тестовые инструменты использования памяти. Эти инструменты очень полезны при тестировании работы программ. Одни из этих инструментов просто сообщают статистику в форме таблиц или графиков, в то время как другие могут обнаружить некоторые ошибки. Их потенциальные возможности таковы.
♦ Утечка памяти.
Определить растущие объемы неиспользуемой памяти, неумышленно возникшие в результате реализации.
♦ Поведение использования памяти.
Подтвердить ожидания.
Определить помехи.
♦ Поведение границ данных.
Например, подтвердить целостность массивов или определить достижение граничных значений.
♦ Инициализация переменных.
Указать неинициализированные переменные.
♦ Перезапись активной памяти.
10. Инструменты управления тестовыми вариантами. Тестовые инструменты управления обычно обладают следующими возможностями [74].
♦ Предоставить пользовательский интерфейс.
Для управления тестами.
♦ Организовать тесты.
Для простоты использования.
Для сопровождения.
♦ Управлять сессиями выполнения тестов.
Для выполнения выбранных пользователем тестов.
♦ Интегрировать с другими тестовыми инструментами.
Для записи и воспроизведения.
Для анализа области охвата.
♦ Предоставить отчеты.
♦ Предоставить документацию.
И. Анализ охвата. Анализаторы охвата получают на вход продукт вместе с тестовым набором. Они предоставляют анализ охвата набора тестов. Эти анализаторы могут также проверить различные типы охвата тестирования, в том числе и охват утверждений.
Тестирование требует повторяющегося использования форм. Шаблоны документов являются наиболее простыми, но наиболее широко используемыми «инструментами» тестирования. Шаблоны могут основываться на стандартах документации по тестированию, например ANSI/IEEE 829-1983 STD (подтвержденных в 1991 году).
Хотя программы автоматического тестирования способны выполнить многие задачи тестового программирования, их использование часто требует значительных программистских навыков. Например, инструменты записи действий мыши и клавиатуры должны отслеживать соответствующие события, а это требует работы программистов с хорошим знанием процесса генерации событий, так чтобы тестовые инструменты могли перехватить работу программы должным образом.
9.8. Подведение итогов.
В этой главе описана фаза интеграции в разработке программы, состоящая из планирования, выполнения и инспектирования сборок. Резюмируем различные формы тестирования, рассмотренные в этой главе.
♦ Процесс интеграции: выполнен по аккуратно спланированным сборкам.
♦ Тестирование интеграции: каждая сборка.
♦ Системное тестирование: программа целиком.
♦ Регрессионное тестирование: проверить, чтобы изменения не компрометировали возможности, существовавшие до внесения изменений.
Упражнения.
Ответы и подсказки для упражнений, помеченных символами «о» или «п», приводятся в конце этой главы.
Вопросы для проверки.
П9.1
. При переходе от одной фазы к следующей часто происходит потеря информации. Назовите от трех до пяти переходов, при которых часто наблюдаются такие потери.
П9.2°. В чем разница между верификацией и валидацией? П9.3°. Назовите от четырех до восьми различных видов тестов и укажите, для чего они необходимы.
П9.4
. Что такое сборки и как они связаны с итерациями? П9.5°. Назовите от трех до пяти артефактов, за которые несут ответственность тестовые инженеры.
П9.6
. Что такое регрессионное тестирование и почему оно необходимо? П9.7
. В чем разница между альфа- и бета-тестированием? П9.8
. Что такое приемосдаточное тестирование?.
П9.9". Назовите от четырех до одиннадцати метрик для интегрального и системного тестирования.
Упражнения в команде.
К9.1. («Интеграция») Получите спецификации проекта от двух других команд в классе. Неформально определите новую программу, содержащую важные элементы этих приложений. Определите план интеграции для сборки этого нового программного приложения. Критерии оценки.
1.
Степень ясности плана («Отлично» — очень ясный, понятный план).
2.
Степень, в которой план содержит подходящий порядок действий («Отлично» — исполнимый и уместный порядок операций).
Общие упражнения.
09.1. На рис. 9.25 показана архитектура программы, эмулирующей обслуживание клиентов банка. Предоставьте план сборки этой программы.
Рис. 9.25. Архитектура из упражнения, посвященного модели банка Критерии оценки.
1.
Степень ясности плана («Отлично» — очень ясный, понятный план).
2.
Степень, в которой план содержит подходящий порядок действий («Отлично» — исполнимый и уместный порядок операций).
09.2. Опишите разные уровни тестирования, которым вы бы подвергли программу из предыдущего упражнения, и укажите, какие из классов, показанных на рис. 9.25, будут участвовать в каждом из этих тестирований. (В создании полной программы будет участвовать гораздо больше классов, но вам не обязательно указывать их все.)
Ответы.
П9.1. Потери информации обычно происходят при преобразовании:.
♦ требований в архитектуру;.
♦ архитектуры в детальный проект;.
♦ архитектуры в спецификации интерфейсов;.
♦ детального проекта в код отдельных функций;.
+ спецификации интерфейса в код модулей.
П9.2. Верификация проверяет, правильно ли мы строим программу. Валидация проверяет, правильную ли программу мы строим.
П9.3. Тесты функций, классов и модулей являются модульными тестами, проверяющими эти физические единицы.
Тесты интерфейсов валидируют способ, которым модули обмениваются информацией друг с другом.
Интегральные тесты валидируют сборки частичной программы.
Системные тесты валидируют работу программы в целом.
Приемосдаточные тесты подтверждают, что программа действительно делает то, что обещали ее разработчики.
Тесты инсталляции подтверждают, что программа работает согласно спецификации в запланированных физических средах.
П9.4. Каждая итерация состоит из последовательности сборок. Каждая сборка — это реализация части программы, разработанная для удобства процесса сборки. Каждая сборка использует в качестве базиса предыдущую сборку.
П9.5. Тестовые варианты, процедуры, планы, оценки и, возможно, модели вариантов использования.
П9.6. Регрессионные тесты разрабатываются для утверждения того факта, что изменение или добавление в коде не испортило имевшиеся раньше возможности. Такие тесты необходимы, поскольку изменения в коде могут полностью изменить поведение программы. Изменения в существующем поведении могут быть результатом дефективных изменений или дефективного существующего проектирования (кода).
П9.7. Альфа-версии даются внутренним пользователям или жестко отобранным представителям заказчика. Бета-версии предоставляются широкому сообществу пользователей.
П9.8. Приемосдаточное тестирование является официальным процессом тестирования, с помощью которого клиент может удостовериться, что продукт отвечает требованиям контракта.
П9.9. Ответ на этот вопрос можно найти в разделе 9.6.2.
Пример 1. План управления конфигурациями программного обеспечения (SCMP). Приложение А: План создания базиса интеграции.
[Примечание для студентов. Нам необходимо описать порядок, в котором программа будет интегрироваться. SCMP является подходящим местом для этого описания, поскольку он описывает конфигурации итераций и сборок.].
История версий этого документа: 1 ноября 1998. Э. Брауде: начальный черновик. 4 апреля 1999. Э. Брауде: внесены исправления.
23 августа 1999. Р. Боствик: просмотрены документы, даны рекомендации.
24 августа 1999. Э. Брауде: рекомендации выполнены.
26 августа 1999. Э. Брауде: просмотрено, расширены комментарии.
1. Введение.
Во время процесса интеграции программа Встреча конструируется по стадиям или сборкам. Это приложение описывает конфигурацию первых трех сборок. Интегральное тестирование основывается на этих сборках. Последняя сборка будет базисом для системного тестирования.
2. Создание базиса интеграции.
Три последовательные сборки первой версии видеоигры Встреча показаны на рис. 9.26. Первая сборка состоит из пакетов ПерсонажиИгры и ПерсонажиВстречи. Вторая сборка использует первую. Она состоит из пакета СредаВстречи, соответствующего ему каркаса и первой сборки. Третья сборка использует первые две. Она состоит из пакета ИграВстреча, соответствующего каркаса, а также сборок 1 и 2.
Рис. 9.26. План интеграции видеоигры Встреча
2.1.
Интеграционная сборка 1.
Сборка 1 показана на рис. 9.27. Сборка 1 реализует каркасные пакеты ПерсонажиИгры и ПерсонажиВстречи.
2.2.
Интеграционная сборка 2.
Сборка 2 показана на рис. 9.28. Она состоит из пакета СредаВстречи и каркасного пакета ИгроваяСреда, а также составляющих первой сборки. Пакетами.
ИгроваяСреда и СредаВстречи используются соответственно классы ПерсонажИгры и ПерсонажВстречи из первой сборки. Двор, подвал и гостиная являются примерами зон. Некоторые из этих зон соединены. Например, существует соединение между гардеробом и двором.
Рис. 9.27. Игра Встреча, сборка 1
Декомпозиция программы, существующая на данный момент, на элементы каркаса показана на рис. 9.29. Пакет ИграВстреча и его каркасный пакет РолеваяИгра отсутствуют на рисунке, поскольку они являются частями третьей сборки.
2.3. Интеграционная сборка 3.
Финальная, третья сборка показана на рис. 9.30. Сборка 3 состоит из пакета ИграВстреча, каркасного пакета РолеваяИгра, сборок 1 и 2.
Рис. 9.28. Встреча, сборка 2
Рис. 9.29. Статус Встречи после второй сборки
Рис. 9.30. Встреча, сборка 3 (содержит сборки 1 и 2 — не показаны)
Пример 2. Документация по тестированию программного обеспечения (STD) для видеоигры Встреча.
История версий этого документа:.
1 ноября 1998. Э. Брауде: начальный черновик. 4 апреля 1999. Э. Брауде: внесены исправления.
23 августа 1999. Р. Боствик: просмотрены документы, даны рекомендации.
24 августа 1999. Э. Брауде: рекомендации выполнены. Статус: следует закончить.
[Примечание для студентов. Этот документ описывает общее тестирование видеоигры Встреча. Документ использует структуру IEEE STD (введение, план, проектирование, тестовые варианты, процедуры, отчет, журнал учета, отчет о происшествиях, итоги) и касается различных частных тестов (интегральные тесты, системные тесты, приемосдаточные тесты и т. д.). Они, в свою очередь, описываются с использованием тех же заголовков IEEE STD.].
1. Введение.
Данный документ содержит STD для видеоигры Встреча и каркас ролевой игры. Категории тестирования, которые затрагивает этот документ, включают в себя.
модульное, интегральное, системное, приемосдаточное и инсталляционное тестирование. Этот документ описывает тестирование, необходимое для валидации первых трех сборок видеоигры Встреча. На каждом уровне тестирования используется стандарт IEEE 829-1983 для STD.
Принцип тестирования для видеоигры Встреча приведен в табл. 9.3.
Таблица 9.3. Подходы и документация тестирования различных типов
Тип.
тестирования
Подход
Соответствующие разделы документов
Модульное
Методы «белого» и «черного ящика»; тестирование методов и классов;тестирование на соответствие D-требованиям и проекту
Разделы SRS: 3.2. Классы/Объекты. Разделы SDD: 6. Детальное проектирование
Интегральное
Метод «серого ящика»; преимущественно пакетный уровень; ориентирован на сборки 1, 2 и 3; тестирование на соответствие архитектуре и С-требованиям
Разделы SRS: 2. Общее описание; 3.1. Требования к внешнему интерфейсу. Валидировать представительные требования в «3.2. Классы/Объекты». Разделы SDD:.
3.
Описание декомпозиции;.
4.
Описание зависимостей;.
5.
Описание интерфейсов
Системное
Метод «черного ящика»; все пакеты; вся система (сборка 3); тестирование на соответствие нефункциональным требованиям, архитектуре и С-требованиям
Разделы SRS: 2. Общее описание; 3.1. Требования к внешнему интерфейсу. Валидировать представительные требования в «3.2. Классы/Объекты»;.
3.3.
Требования к производительности;.
3.4.
Ограничения проектирования;.
3.5.
Атрибуты программной системы;.
3.6.
Дополнительные требования Разделы SDD:.
3.
Описание декомпозиции;.
4.
Описание зависимостей;.
5.
Описание интерфейсов; Валидировать представительные требования в «6. Детальное проектирование»
Приемосдаточное
Метод «черного ящика»; все пакеты; вся система (сборка 3); тестирование на соответствие С-требованиям и D-требованиям
Разделы SRS: 2. Общее описание; 3.2. Классы/Объекты
Инсталляционное
Метод «черного ящика»; все пакеты; вся система (сборки для конкретных конфигураций заказчика); тестирование на соответствие С-требованиям и D-требованиям
Разделы SRS: 2. Общее описание; 3.2. Классы/Объекты
[Примечание для студентов. Это применение рис. 9.5 к видеоигре Встреча. SDD не считается документом с требованиями к проектированию, но предъявляет требования к реализации. Иногда эти требования формулируются в отдельном документе. В примере, рассматриваемом в этой книге, не имеется отдельного документа, содержащего эти требования.].
2. Документация тестирования видеоигры Встреча.
STD для видеоигры Встреча и каркаса ролевой игры охватывает план тестирования, спецификацию и отчетность. Существуют отдельные планы тестирования для разных типов тестирования (модульного, интегрального, системного, приемосдаточного и инсталляционного). Каждый план тестирования ссылается на свои спецификации проекта тестирования, тестовых вариантов и тестовых процедур. Отчетная документация по тестированию состоит из журнала испытаний, отчета о происшествиях и итогового отчета.
2.1.
STD модульного тестирования.
См. отдельный документ по модульному тестированию. [Примечание для студентов. См. пример в главе 8.].
2.2.
STD интегрального тестирования.
Документация интегрального тестирования состоит из отдельных документов для сборок 1, 2 и 3, как будет описано далее. См. приложение А к SCMP для создания базиса интеграции.
2.2.1. STD сборки 1.
2.2.1.1. План тестирования сборки 1.
2.2.1.1.1.
Идентификатор плана тестирования: Сборка1_ПТ.
2.2.1.1.2.
Введение.
Данный план тестирования охватывает интегральные тесты для каркасного пакета ПерсонажиИгры и пакета ПерсонажиВстречи. Он описывает, как проверить, что персонаж игрока и внешний персонаж можно вызвать, модифицировать и показать с помощью одиночного объекта РолиВстречи.
2.2.1.1.3.
Тестовые элементы.
Классы и методы из пакетов ПерсонажиИгры и ПерсонажиВстречи тестируются через объект РолиВстречи.
2.2.1.1.4.
Свойства, подлежащие тестированию.
Свойства, тестируемые согласно спецификации проекта тестирования Сборка1_ СП, основываются на требованиях SRS и SDD (табл. 9.4).
Таблица 9.4. Свойства, подлежащие тестированию в сборке 1
Документ
Раздел
Название требования
SRS
2.1.2.2
Концепция пользовательского интерфейса для установки значений характеристик
3.2.ПВ
Персонаж Встречи
3.2.ВП
Внешние персонажи
3.2.ПИ
Персонажи игрока
3.2.ХИ
Окно характеристик игрока
SDD для каркаса ролевой игры
3.1.2
Пакет Персонажи
5
Описание интерфейса
SDD для игры Встреча
3.1.2
Пакет ПерсонажиВстречи
4.2
Межпроцессные зависимости
5.1.2
Интерфейс пакета ПерсонажиВстречи
2.2.1.1.5.
Свойства, не подлежащие тестированию.
[Примечание для студентов. Существует бесконечно много вопросов, которые не тестируются, однако иногда определение некоторых конкретных вопросов, не подлежащих тестированию, помогает прояснить процесс тестирования.].
Тестирование характеристик, ассоциирующихся с пакетами СредаВстречи и ИграВстреча и их каркасами, откладывается до интегрального тестирования сборок 1 и 2.
2.2.1.1.6.
Подход.
Подход в верификации сборки 1 состоит из проверки того, что все персонажи игры можно вызвать и показать с помощью объекта РолиВстречи. Тесты методов и интерфейсов проверяют, доступны ли необходимые открытые (public) методы интерфейсов пакета ПерсонажиВстречи объекту РолиВстречи.
2.2.1.1.7.
Критерий успешного прохождения теста.
Критерий успешного прохождения основан на удовлетворении соответствующих требований в SRS и SDD.
2.2.1.1.8.
Критерий приостановки и требования продолжения.
(Отсутствуют.).
2.2.1.1.9.
Тестовые отчеты, подлежащие сдаче.
Документы, перечисленные в табл. 9.5, следует сдать группе управления конфигурациями по завершении интегрального тестирования сборки 1.
Таблица 9.5. Идентификаторы документов тестирования
Документ
Идентификатор документа
Сборка 1: План тестирования
Сборка1_ЛТ
Сборка 1: Спецификация проекта тестирования Сборка 1: Спецификации тестовых вариантов
Сборка1_СП.
Сборка1_ТВ1— Сборка1_ТВп
Таблица 9.5 (продолжение)
Документ
Идентификатор документа
Сборка 1: Спецификации тестовых процедур
Сборка1_ТП1—Сборка1_ТПп
Сборка 1: Журналы тестовых испытаний
Сборка1_ЖТ1—Сборка1_ЖТп
Сборка 1: Отчет о происшествиях во время тестирования
Сборка1 _ОП 1 —Сборка1 _ОПп
Сборка 1: Итоговый отчет
Сборка! _И01—Сб0рка1_И0п
2.2.1.1.10.
Задачи тестирования.
Задачи состоят из следующих шагов.
1.
Загрузить сборку 1 и пакет Build_1.
2.
Выполнить тестовые процедуры сборки 1 из метода mainO в Build_1Test в пакете Build_1.
3.
Составить отчетную документацию по тестированию в соответствии с разделом 2.2.1.1.9.
4.
Сохранить всю документацию по тестированию и данные в соответствии с разделом 2.2.1.1.9 в управлении конфигурациями.
2.2.1.1.11.
Требования среды.
В зависимости от доступности оборудования можно использовать рабочие станции IBM PC, Sun SPARC или аппаратную конфигурацию Apple IMAC. Для тестирования сборки 1 может использоваться интерактивная среда разработки IBM Visual Age.
2.2.1.1.12.
Ответственность.
Салли Сильвер и Джоз Хернандес из группы контроля качества (SQA) отвечают за управление, подготовку и проведение интегрального тестирования сборки 1. Кроме того, группе разработчиков видеоигры Встреча направляются технические вопросы и отчеты о происшествиях во время тестирования. Управление конфигурациями сохраняет всю документацию по тестированию и данные.
2.2.1.1.13.
Требования к персоналу и обучение.
SPMP определяет общие потребности в персонале и тренинге для интегрального тестирования.
2.2.1.1.14.
План.
План интегрального тестирования включен в раздел 5.5 версий 5 и выше SPMP. (В разделе 5.5.5 обсуждается обновление SPMP для поддержания его соответствия выбранной архитектуре.).
[Примечание для студентов. В примерах этой книги не приводится обновленный SPMP.].
2.2.1.1.15.
Риск и непредвиденные обстоятельства.
Если группа контроля качества не может выполнить тесты или ошибки приводят к неприемлемому количеству сбоев в системе, группе Альфреда Муррея из команды разработчиков видеоигры Встреча будет поручено провести интегральное тестирование сборки 1.
2.2.1.1.16. Утверждение.
Для завершения этой части тестирования требуется подписание утверждающего документа руководителем группы контроля качества, менеджером по разработке видеоигры Встреча и представителем группы управления изменениями.
2.2.1.2.
Проект тестирования сборки 1.
2.2.1.2.1.
Идентификатор спецификации проекта тестирования.
Следует определить.
2.2.1.2.2.
Свойства, подлежащие тестированию.
Тест для сборки 1 получает объект РолиВстречи, ПерсонажИгрока и ВнешнийПерсонаж, изменяет значения различных характеристик, получает эти значения, проверяет их корректность.
2.2.1.2.3.
Обновления в подходе.
Следует определить.
2.2.1.2.4.
Идентификация теста.
Следует определить.
2.2.1.2.5.
Критерий успешного прохождения тестирования свойств.
Следует определить.
2.2.1.3.
Тестовые варианты сборки 1.
2.2.1.3.1.
Идентификатор спецификации тестовых вариантов.
Следует определить.
2.2.1.3.2.
Тестовые элементы.
Функциональность, которую следует протестировать, находится в спецификации следующих открытых методов объекта РолиВстречи:.
EncounterCast getTheEncounterCast().
GameCharacter getThePIayerCharacter().
GameCharacter gethTheForeignCharacter().
void setPlayerChracterQua1ity(String quality, float value).
void setForeignCharacterQuality(String quality, float value).
float getPlayerCharacterQuality().
float getForeignCharacterQuality().
Их следует протестировать в соответствии с табл. 9.6.
Таблица 9.6. Входные-выходные данные и действия интегральных тестов
Характеристика
Входное.
значение.
игрока
Входное значение внешнего персонажа
Другие
Действие
В 1.1 Отсутствует
Отсутствует
Отсутствует
Получить персонаж игрока
Проверить по имени
Таблица 9.6 (продолжение)
Характеристика
Входное.
значение.
игрока
Входное значение внешнего персонажа
Другие
Действие
В1.2
Отсутствует
Отсутствует
Отсутствует
Получить внешний персонаж
Проверить по имени
В1.3
Сосредоточенность
30
40
Отсутствует
Проверить, что выходные данные равны входным
В1.4
Выносливость
30
40
Отсутствует
Проверить, что выходные данные равны входным
В1.5
2.2.1.3.3.
Спецификация входных данных.
См. табл. 9.6.
2.2.1.3.4.
Спецификация выходных данных.
См. табл. 9.6.
2.2.1.3.5.
Требования к среде.
Это тестирование выполняется только для пакетов ПерсонажиИгры и ПерсонажиВстречи.
2.2.1.3.6.
Специальные требования к процедурам.
Нет.
2.2.1.3.7.
Зависимость от интерфейса.
Нет.
[Примечание для студентов. Этот раздел описывает связь между разными интерфейсами. Это будет важно для будущих сборок, но не для первой сборки.].
2.2.1.4. Тестовые процедуры сборки 1.
2.2.1.4.1.
Идентификатор спецификации тестовых процедур.
[Примечание для студентов. Указывает на класс (метод), из которого следует запускать тест.].
Integration_Tests/Buildl_Test в пакете Tests.
2.2.1.4.2.
Цель.
Установить тест сборки 1 с минимальным количеством других частей программы.
2.2.1.4.3.
Специальные требования.
Следует создать тест в Integration_tests/Buildl_Test, состоящий из класса с одним методом main(). Тесты 1, 2,3... следует выполнить, а полученные результаты сравнить.
2.2.1.4.4. Процедурные шаги.
Разместите входные данные и ожидаемые результаты характеристик в файле Buildl_test_data в следующем формате:.
.
Не должно быть никакого дополнительного текста в начале или в конце.
2.2.1.5.
Отчет о проведении тестирования сборки 1.
2.2.1.5.1.
Идентификатор отчета о проведении тестирования.
Сборка 1 _И01.
2.2.1.5.2.
Посланные элементы.
Итоговый отчет о тестировании, журнал испытаний, отчет о происшествиях.
2.2.1.5.3.
Местоположение.
Документация для сборочного теста 1 находится...
2.2.1.5.4.
Статус.
Еще не выполнен.
2.2.1.5.5.
Утверждения.
Тестирование сборки 1 должно быть утверждено менеджером контроля качества.
2.2.1.6.
Журнал испытаний сборки 1.
2.2.1.6.1.
Идентификатор журнала испытаний.
Сборка1_ЖТ1.
2.2.1.6.2.
Описание.
Этот документ описывает результаты тестирования сборки 1 (пример в табл. 9.7). Таблица 9.7. Журнал испытаний сборки 1
Номер теста
Результат
Ссылка на ошибку
1
Пройден
Отсутствует
2
Не пройден
1823
3
Потеря данных — повторить
Отсутствует
4
Потеря точности в возвращенном значении
2872
5
2.2.1.6.3. Записи действий и событий.
[Примечание для студентов. «Ссылка на дефект» — это число, используемое системой отслеживания дефектов для данного конкретного дефекта.].
2.2.1.7.
Отчет о происшествиях во время тестирования сборки 1.
2.2.1.7.1.
Идентификатор отчета о происшествиях во время тестирования.
Сборка1_ОПЗ.
2.2.1.7.2.
Итоги.
См. табл. 9.7.
2.2.1.7.3.
Описание происшествий.
Эд Блэйк отвлекся при выполнении теста 3 на сработавшую в здании сигнализацию и не смог записать результаты теста. Было решено не прерывать и не повторять тестовую последовательность и включить тест 3 в тестирование для сборки 2.
2.2.1.7.4.
Влияние.
Было решено, что события, описанные выше, недостаточно серьезны для того, чтобы повторно запускать этот тест.
2.2.1.8.
Итоговый отчет о тестировании сборки 1.
2.2.1.8.1.
Идентификатор итогового отчета о тестировании.
Следует определить.
2.2.1.8.2.
Итоги.
Тестирование сборки 1 прошло успешно, за исключением отмеченных дефектов. Они будут обработаны в обычном процессе исправления дефектов.
2.2.1.8.3.
Конфликты.
См. отчет о происшествиях во время тестирования сборки 1.
2.2.1.8.4.
Полная оценка.
Будет приложено.
[Примечание для студентов. Дополнительные примечания, подробности.] Нет.
2.2.1.8.5.
Подведение итогов по результатам.
Будет приложено.
2.2.1.8.6.
Оценка.
Будет приложено.
2.2.1.8.7.
Подведение итогов действиям.
Будет приложено.
2.2.1.8.8.
Утверждения.
_Утверждаю Не утверждаю.
Менеджер контроля качества.
2.2.2.
STD сборки 2.
(Формат аналогичен формату STD для сборки 1.).
2.2.2.1.
План тестирования сборки 2.
Эти тесты будут проверять, что все зоны игры можно вызвать и показать через объект СредаВстречи и что соединения между зонами согласуются с SRS.
2.2.2.2.
Проект тестирования сборки 2.
Эти тесты будут прежде всего проверять получение корректного объекта СредаВстречи, а затем показывать, что объекты Зона и СоединениеЗоны можно получить по требованию.
2.2.2.3.
Тестовые варианты сборки 2.
Тестируемая функциональность содержится в приведенных ниже открытых функциях класса СредаВстречи.
GameArea getTheDressingRoom() GameArea getTheDungeon().
EncounterEnvi ronment getTheEncounterEnvi ronment().
2.2.2.4.
Тестовые процедуры сборки 2.
Следует добавить.
2.2.2.5.
Поэлементный отчет о проведении тестирования сборки 2.
Следует добавить.
2.2.2.6.
Журнал испытаний сборки 2.
Следует добавить.
2.2.2.7.
Отчет о происшествиях во время тестирования сборки 2.
Следует добавить.
2.2.2.8.
Итоговый отчет о тестировании сборки 2.
Следует добавить.
2.2.3.
STD сборки 3.
Следует добавить.
2.3. STD системного тестирования.
[Примечание для студентов. Вспомните (см. рис. 9.6), что системные тесты проверяют корректность реализации архитектуры.].
2.3.1. План системного тестирования.
[Примечание для студентов. Эти тесты проверяют архитектуру (рис. 9-31).] Эти тесты проверяют, что результаты действий в игре Встреча корректно проявляют себя в виде перемещений персонажей в среде.
Рис. 9.31. Архитектура модуляризации видеоигры Встреча
2.3.2.
Проект системного тестирования.
[Примечание для студентов. Системные тесты разработаны для верификации архитектуры путем выполнения и проверки последовательностей интерфейсных методов.].
2.3.3.
Тестовые варианты.
Системный тест 1.
1.
Переместить персонаж игрока в подвал.
2.
Переместить внешний персонаж во двор.
3.
Переместить внешний персонаж в подвал.
4.
Выполнить встречу в подвале. Системный тест 2.
1. ...
2.3.4.
Тестовые процедуры.
Системные тесты выполняются из пакета SystemTest. Системный тест Лвыполняется посредством метода mainO класса SystemTestN.
2.4. STD приемосдаточного тестирования.
2.4.1. План приемосдаточного тестирования.
[Примечание для студентов. Интегральные тесты проверяют, что требования к игре Встреча удовлетворены couacnoSRS.].
Приемосдаточные тесты хранятся в пакете AcceptanceTest и содержат варианты использования.
Вариант использования «Инициализировать» показан на рис. 9.32 и выполняется из метода mainO класса Initialize пакета AcceptanceTest.
Рис. 9.32. Диаграмма последовательности для варианта использования «Инициализировать»
Вариант использования «Встретить внешний персонаж» показан на рис. 9.33 и выполняется из метода mainO класса AcceptanceTest.Initialize.
Рис. 9.33. Диаграмма последовательности для варианта использования «Встретить внешний персонаж»
2.4.2.
Проект приемосдаточного тестирования.
Варианты использования, упомянутые в разделе 2.4.1, должны быть выполнены последовательно несколько раз в соответствии с тестовыми вариантами из раздела 2.4.3.
2.4.3.
Тестовые варианты.
2.4.3.1.
Тестовые варианты для варианта использования «Инициализировать».
[Примечание для студентов. Тесты являются экземплярами варианта использования «Инициализировать», называемыми также «сценариями».].
2.4.3.1.1.
Приемосдаточный тест 1 «Инициализировать».
Запустить игру.
Назначить главному персонажу следующие значения характеристик в указанном порядке:.
♦ Сила: 30.
♦ Сосредоточенность: 20 Переместить главный персонаж во двор.
2.4.3.1.2.
Приемосдаточный тест 2 «Инициализировать».
Запустить игру.
Назначить главному персонажу следующие значения характеристик в указанном порядке:.
♦ Сила: 30.
+ Сосредоточенность: 20.
♦ Терпение: 30.
Переместить главный персонаж во двор.
2.4.3.1.3.
Приемосдаточный тест 3 «Инициализировать».
Запустить игру.
Назначить главному персонажу следующие значения характеристик в указанном порядке:.
♦ Сила: 30.
♦ Сосредоточенность: 20 Переместить главный персонаж в подвал.
2.4.3.1.4.
Приемосдаточный тест 4 «Инициализировать».
2.4.3.2.
Тестовые варианты для варианта использования «Встретить внешний персонаж».
2.4.3.2.1. Приемосдаточный тест 1 «Встретить внешний персонаж».
Установить значение терпения главного персонажа равным 30. Установить значение терпения внешнего персонажа равным 20.
Переместить главный персонаж в гостиную. Заставить внешний персонаж войти в гостиную. Убедиться, что контакт имел место. Пронаблюдать окно контакта с результатами.
(Значение терпения игрока должно стать 40, внешнего персонажа — 10).
2.4.3.2.2. Приемосдаточный тест 2 «Встретить внешний персонаж».
Установить значение силы главного персонажа равным 30. Установить значение силы внешнего персонажа равным 20. Переместить главный персонаж в подвал. Заставить внешний персонаж войти в подвал. Убедиться, что контакт имел место. Пронаблюдать окно контакта с результатами.
(Значение силы игрока должно стать 40, внешнего персонажа — 10). 2.4.3.2.3 Приемосдаточный тест 3 «Встретить внешний персонаж».
2.4.4. Тестовые процедуры.
Приемосдаточные тесты будут проводиться двумя представителями заказчика. Эти представители будут проводить тестирование при участии представителя фирмы-разработчика. По возможности события, такие как появление внешнего персонажа в зоне, должны возникать как результат случайных процессов игры, а не эмуляции. Журнал испытаний должны вести представители заказчика. По окончании тестов он должен быть подписан всеми сторонами: любая сторона может добавить в журнал свои возражения.
2.5. STD тестирования инсталляции.
[Примечание для студентов. Эти тесты проверяют, корректно ли работает программа на требуемой аппаратной конфигурации и в среде операционной системы.] Тестирование инсталляции видеоигры Встреча состоит из выполнения системных тестов на следующих аппаратных конфигурациях.
1.
IBM-совместимый ПК, на котором имеется не менее 32 Мбайт RAM и 100 Мбайт дискового пространства.
2.
Sun SPARC модель mmm не менее чем с 32 Мбайт RAM и 100 Мбайт дискового пространства.
3.
Apple IMAC модель 1234 или более поздняя не менее чем с 32 Мбайт RAM и 100 Мбайт дискового пространства.