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

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

  Итак, что мы ищем? Наша задача как тестировщиков — искать ошибки. Где они обычно скрываются? Чем отличаются программы, которые чаще всего содержат ошибки?

• Неуловимые зависимости от другого кода. Ищите использование глобальных переменных, аргументы, которые передаются не с помощью константных ссылок, указатели и т.п.

• Управление ресурсами. Обратите внимание на управление памятью (операторы new и delete), использование файлов, блокировки и т.п.

• Поищите циклы. Проверьте условия выхода из них (как в функции binary_search()).

• Инструкции if и switch (которые часто называют инструкциями ветвления). Ищите ошибки в их логике.

Рассмотрим примеры, иллюстрирующие каждый из перечисленных пунктов.

<p id="AutBody_Root518"><strong>26.3.3.1. Зависимости</strong></p>

Рассмотрим следующую бессмысленную функцию.

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] может находиться за пределами допустимого диапазона).

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

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже