Под явным или неявным мы подразумеваем, что следует проверять не только формальные параметры и возвращаемое значение, но и влияние глобальных переменных, потоки ввода-вывода, файлы, распределение свободной памяти и т.д. Что же мы можем сделать? Во-первых, такая функция практически всегда бывает очень длинной, иначе ее требования и действия можно было бы описать более точно. Возможно, речь идет о функции длиной около пяти страниц или функции, использующей вспомогательные функции сложным и неочевидным способом. Для функции пять страниц — это много. Тем не менее мы видели функции намного-намного длиннее. К сожалению, это не редкость.
• Неуловимые зависимости от другого кода. Ищите использование глобальных переменных, аргументы, которые передаются не с помощью константных ссылок, указатели и т.п.
• Управление ресурсами. Обратите внимание на управление памятью (операторы new
и delete
), использование файлов, блокировки и т.п.
• Поищите циклы. Проверьте условия выхода из них (как в функции binary_search()
).
• Инструкции if
и switch
(которые часто называют инструкциями ветвления). Ищите ошибки в их логике.
Рассмотрим примеры, иллюстрирующие каждый из перечисленных пунктов.
26.3.3.1. Зависимости
Рассмотрим следующую бессмысленную функцию.
int do_dependent(int a,int& b) // плохая функция
// неорганизованные зависимости
{
int val;
cin>>val;
vec[val] += 10;
cout << a;
b++;
return b;
}
Для тестирования функции do_dependent()
мы должны не просто синтезировать набор аргументов и посмотреть, что она с ними будет делать. Мы должны учесть, что эта функция использует глобальные переменные cin
, cout
и vec
. Это обстоятельство вполне очевидно в данной небольшой и бессмысленной программе, но в более крупном коде оно может быть скрыто. К счастью, существует программное обеспечение, позволяющее находить такие зависимости. К несчастью, оно не всегда доступно и довольно редко используется. Допустим, у нас нет программного обеспечения для анализа кода и мы вынуждены строка за строкой просматривать функцию в поисках ее зависимостей.
Для того чтобы протестировать функцию do_dependent()
, мы должны проанализировать ряд ее свойств.
• Входные данные функции
• Значение переменной a
.
• Значения переменной b
и переменной типа int
, на которую ссылается переменная b
.
• Ввод из потока cin
(в переменную val
) и состояние потока cin
.
• Состояние потока cout
.
• Значение переменной vec
, в частности значение vec[val]
.
• Выходные данные функции
• Возвращаемое значение.
• Значение переменной типа int
, на которую ссылается переменная b
(мы ее инкрементировали).
• Состояние объекта cin
(проверьте состояния потока и формата).
• Состояние объекта cout
(проверьте состояния потока и формата).
• Состояние массива vec
(мы присвоили значение элементу vec[val]
).
• Любые исключения, которые мог сгенерировать массив vec
(ячейка vec[val]
может находиться за пределами допустимого диапазона).