Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 796
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
502
ЧАСТЬ V Усовершенствование кода му из законов Мерфи, программа потерпит крах тогда, когда это причинит наибольший ущерб.
쐽
Использование — определение Определение переменной после использо- вания может быть проблемой, а может и не быть. Это зависит от того, была ли переменная также определена до ее использования. Если вы встречаете такую комбинацию, обязательно проверьте наличие предшествующего определения.
Проверьте код на предмет этих аномальных изменений состояния данных до начала тестирования. После их проверки написание тестов, основанных на потоке дан- ных, сводится к анализу всех возможных комбинаций «определение — использо- вание». Выполнить это можно с различной степенью тщательности.
쐽
Проверка всех определений. Протестируйте каждое определение каждой пе- ременной, т. е. все места, в которых какая-либо переменная получает свое зна- чение. Это слабая стратегия, поскольку, если вы тестируете каждую строку кода,
вы и так выполните это.
쐽
Проверка всех комбинаций «определение — использование». Протестируйте все комбинации, включающие определение переменной в одном месте и вы- зов в другом. Это более грамотная стратегия, так как простое тестирование каждой строки кода далеко не всегда обеспечивает проверку всех комбинаций
«определение — использование».
Вот пример:
Пример программы, поток данных которой мы протестируем (Java)
if ( Condition 1 ) {
x = a;
}
else {
x = b;
}
if ( Condition 2 ) {
y = x + 1;
}
else {
y = x - 1;
}
Для покрытия каждого пути выполнения этой программы нам нужен один тест,
при котором условие
Condition 1 истинно, и один — при котором оно ложно, а также аналогичные тесты для условия
Condition 2. Эти ситуации можно охватить двумя тестами:
(Condition 1=True, Condition 2=True) и (Condition 1=False, Condition
2=False). Два этих теста — все, что нужно для выполнения требований структури- рованного базисного тестирования, а также для тестирования всех определений переменных; эти тесты автоматически обеспечивают слабую форму тестирования,
основанного на потоках данных.
Однако для покрытия всех комбинаций «определение — использование» этого мало.
На текущий момент у нас есть тесты тех случаев, когда условия
Condition 1 и
Condition 2 истинны и когда оба они ложны:
1 ... 57 58 59 60 61 62 63 64 ... 104
ГЛАВА 22 Тестирование, выполняемое разработчиками
503
x = a y = x + 1
и x = b y = x - 1
Но у нас есть еще две комбинации «определение — использование»: (1)
x = a, после чего
y = x - 1 и (2) x = b, после чего y = x + 1. В данном примере эти комбинации можно покрыть, добавив еще два теста:
(Condition 1=True, Condition 2=False) и
(Condition 1=False, Condition 2=True).
Можно порекомендовать следующую стратегию разработки тестов: начните со структурированного базисного тестирования, которое охватит некоторые, если не все потоки данных «определение — использование». После этого добавьте те- сты, нужные для полного охвата комбинаций «определение — использование».
Структурированное базисное тестирование указало нам на шесть тестов метода,
для которого мы подсчитывали число тестов. Тестирование каждой пары «опре- деление — использование» требует нескольких дополнительных тестов. Вот пары,
которые не охватываются уже созданными тестами:
Номер теста
Описание теста
7
Определение переменной
companyRetirement в строке 12 и использо- вание в строке 26. Этот случай может не покрываться имеющимися тестами.
8
Определение переменной
companyRetirement в строке 12 и использо- вание в строке 31. Этот случай может не покрываться имеющимися тестами.
9
Определение переменной
companyRetirement в строке 17 и использо- вание в строке 31. Этот случай может не покрываться имеющимися тестами.
Выполнив тестирование, основанное на потоках данных, несколько раз, вы нач- нете чувствовать, какие тесты нужны, а какие уже реализованы. В случае сомне- ний ищите все комбинации «определение — использование». Это может показаться слишком объемной работой, но так вы точно найдете все случаи, не охваченные базисным тестированием.
Разделение на классы эквивалентности
Хороший тест должен покрывать широкий диапазон вход- ных данных Если два теста приводят к обнаружению одних и тех же ошибок, вам нужен лишь один из них. Понятие
«разделения на классы эквивалентности» формализует эту идею и помогает уменьшить число нужных тестов.
Подходящее место для разделения на классы эквивалентно- сти — строка 7 уже известного нам листинга, в которой проверяется условие
m_employee[ ID ].governmentRetirementWithheld < MAX_GOVT_RETIREMENT
. Это условие
Перекрестная ссылка Разделе- ние на классы эквивалентнос- ти гораздо подробнее обсужда- ется в книгах, указанных в раз- деле «Дополнительные ресур- сы» в конце этой главы.
504
ЧАСТЬ V Усовершенствование кода делит все значения
m_employee[ ID ]. governmentRetirement Withheld на два класса эквивалентности: значения, которые меньше константы
MAX_GOVT_RETIREMENT,
и значения, которые больше или равны ей. В других частях программы могут использоваться другие классы эквивалентности, что может потребовать тестиро- вания более двух значений
m_employee[ ID ].governmentRetirementWithheld, но в этой части программы нужно проверить только два значения.
Размышление о разделении на классы эквивалентности не скажет вам много но- вого о программе, если вы уже покрыли ее базисным тестированием и тестиро- ванием, основанным на потоках данных. Однако оно очень полезно, если вы смот- рите на программу «извне» (с точки зрения спецификации, а не исходного кода)
или если данные сложны, а эта сложность плохо отражена в логике программы.
Угадывание ошибок
Формальные методики тестирования хорошие программи- сты дополняют менее формальными эвристическими мето- диками. Одна из них — угадывание ошибок (error guessing).
Термин «угадывание ошибок» — довольно примитивное название вполне разум- ной идеи, подразумевающей создание тестов на основе обдуманных предположе- ний о вероятных источниках ошибок.
Предположения можно выдвигать, опираясь на интуицию или накопленный опыт.
Так, в главе 21 в числе прочих достоинств инспекций были указаны создание и обновление списка частых ошибок, используемого при проверке нового кода.
Поддерживая списки ранее допущенных ошибок, вы повысите эффективность своих догадок.
Ниже рассматриваются конкретные виды ошибок, угадать которые легче всего.
Анализ граничных условий
Одной из самых плодотворных областей тестирования являются граничные ус- ловия — ошибки занижения или завышения на 1. Действительно, разработчики очень часто используют
num – 1 вместо num или >= вместо >.
Идея анализа граничных условий состоит в написании тестов, позволяющих про- верить эти условия. Так, при тестировании диапазона значений, которые меньше
max, возможны три условия:
Как видите, в этой ситуации мы имеем три граничных случая: максимальное зна- чение, которое меньше
max, само значение max и минимальное значение, пре- вышающее
max. Для исключения распространенных ошибок нужны три теста.
Фрагмент кода, для которого мы подсчитывали число тестов, содержит проверку
m_employee[ ID ].governmentRetirementWithheld < MAX_GOVT_RETIREMENT. Согласно принципам анализа граничных условий следует изучить три случая:
Перекрестная ссылка Об эвристи- ческих методиках см. раздел 2.2.
ГЛАВА 22 Тестирование, выполняемое разработчиками
505
Номер теста
Описание теста
1
Тест требует, чтобы истинное значение выражения
m_employee[ ID
]. governmentRetirementWithheld < MAX_GOVT_RETIREMENT было первым с истинной стороны границы. Иначе говоря, мы должны присвоить элементу
m_employee[ ID ].governmentRetirementWithheld значение
MAX_GOVT_RETIREMENT - 1. Для этого годится уже имеющийся тест 1.
3
Тест требует, чтобы ложное значение выражения
m_employee[ ID ]. go
vernmentRetirementWithheld < MAX_GOVT_RETIREMENT было первым с ложной стороны границы. Таким образом, элементу
m_employee[ ID
].governmentRetirementWithheld нужно присвоить значение MAX_GOVT_
RETIREMENT + 1. Для этого вполне подойдет тест 3.
10
Дополнительный тест нужен для самой границы, когда
m_employee
[ ID ].governmentRetirementWithheld = MAX_GOVT_RETIREMENT.
Сложные граничные условия
Анализ граничных условий можно проводить также в отношении минимального и максимального допустимых значений. В нашем примере ими могли бы быть минимальные или максимальные значения переменных
grossPay, companyRetirement
или
personalRetirement, но из-за того, что эти значения вычисляются вне области видимости метода, их тестирование мы обсуждать не будем.
Более тонкий вид граничного условия имеет место, когда оно зависит от комби- нации переменных. Например, что произойдет при умножении двух переменных,
если обе являются большими положительными числами? Большими отрицатель- ными числами? Если хотя бы одна из переменных равна 0? Что, если все пере- данные в метод строки имеют необычно большую длину?
В текущем примере вы могли бы проверить, что происходит с денежными сум- мами, которые представлены переменными
totalWithholdings, totalGovernment-
Retirement и totalRetirement, если каждый член большой группы имеет крупную зарплату — скажем, каждый из программистов зарабатывает по 250 000 долларов
(надежда умирает последней!). Для этого нужен еще один тест:
Номер теста
Описание теста
11
Большая группа высокооплачиваемых сотрудников (конкретные пока- затели зависят от конкретной системы — скажем, 1000 сотрудников,
каждый из которых зарабатывает по 250 000 долларов в год), не вы- плачивающих взносы в фонд социального страхования, но отчисляю- щих средства в пенсионный фонд компании.
Противоположным тестом из этой же категории было бы вычисление всех пока- зателей для небольшой группы сотрудников, не получающих зарплату:
Номер теста
Описание теста
12
Группа из 10 сотрудников, не получающих зарплату.
Классы плохих данных
Кроме граничных условий, программу можно тестировать на предмет несколь- ких других классов плохих данных. Типичными примерами плохих данных мож- но считать:
506
ЧАСТЬ V Усовершенствование кода
쐽
недостаток данных (или их отсутствие);
쐽
избыток данных;
쐽
неверный вид данных (некорректные данные);
쐽
неверный размер данных;
쐽
неинициализированные данные.
Некоторые из этих случаев уже покрыты имеющимися тестами. Так, «недостаток данных» охватывается тестами 2 и 12, а для «неверного размера данных» тесты придумать трудно. И все же рассмотрение классов плохих данных позволяет со- здать еще несколько тестов:
Номер теста
Описание теста
13
Массив из 100 000 000 сотрудников. Тестирование на предмет избытка данных. Конечно, объем данных, который следует считать избыточ- ным, зависит от конкретной системы.
14
Отрицательная зарплата. Неверный вид данных.
15
Отрицательное число сотрудников. Неверный вид данных.
Классы хороших данных
При поиске ошибок легко упустить из виду тот факт, что номинальный случай так- же может содержать ошибку. Примерами хороших данных могут служить номиналь- ные случаи, описанные в разделе, посвященном базисному тестированию. Ниже ука- заны другие виды хороших данных, которые стоит подвергать проверке; проверка каждого из этих видов данных также может приводить к обнаружению ошибок:
쐽
номинальные случаи: средние, ожидаемые значения;
쐽
минимальная нормальная конфигурация;
쐽
максимальная нормальная конфигурация;
쐽
совместимость со старыми данными.
Минимальную нормальную конфигурацию полезно применять для тестирования не только одного элемента, но и группы элементов. Минимальная нормальная конфигурация аналогична граничному условию, составленному из нескольких минимальных значений, но отличается от него тем, что она включает набор ми- нимальных значений из диапазона нормально ожидаемых значений. В качестве примера можно привести сохранение пустой электронной таблицы. При тести- ровании текстового редактора примером могло бы быть сохранение пустого до- кумента. В нашем примере тестирование минимальной нормальной конфигура- ции добавило бы в набор следующий тест:
Номер теста
Описание теста
16
Группа из 1 сотрудника. Тестирование минимальной нормальной конфигурации.
Максимальная нормальная конфигурация противоположна минимальной. Она также аналогична граничному условию, но опять-таки включает набор максималь- ных значений из диапазона ожидаемых значений. Пример — сохранение или