Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Прежде чем идти дальше, отметим еще один пример (полуформальный) методики тестирования: мы тестировали правильные значения, иногда выбирая их из конца последовательности, а иногда из середины. Для данной последовательности мы можем перебрать все ее значения, но на практике сделать это нереально. Для тестов, ориентированных на провал, выбираем одно значение в каждом из концов последовательности и одно в середине. И снова следует отметить, что этот подход не является систематическим, хотя он демонстрирует широко распространенный образец, которому можно следовать при работе с последовательностями или диапазонами значений.

Какими недостатками обладают указанные тесты?

• Один и тот же код приходится писать несколько раз.

• Тесты пронумерованы вручную.

• Вывод минимальный (мало информативный).

Поразмыслив, мы решили записать тесты в файл. Каждый тест должен иметь идентифицирующую метку, искомое значение, последовательность и ожидаемый результат. Например:

{ 27 7 { 1 2 3 5 8 13 21} 0 }

Это тест под номером 27. Он ищет число 7 в последовательности { 1,2,3,5,8,13,21 }, ожидая, что результатом является 0 (т.е. false). Почему мы записали этот тест в файл, а не в текст программы? В данном случае мы вполне могли написать этот тест прямо в исходном коде, но большое количество данных в тексте программы может ее запутать. Кроме того, тесты часто генерируются другими программами. Как правило, тесты, сгенерированные программами, записываются в файлы. Кроме того, теперь мы можем написать тестовую программу, которую можно запускать с разными тестовыми файлами.

struct Test {

  string label;

  int val;

  vector seq;

  bool res;

};

istream& operator>>(istream& is, Test& t); // используется описанный

                                           // формат

int test_all(istream& is)

{

  int error_count = 0;

  Test t;

  while (is>>t) {

    bool r = binary_search( t.seq.begin, t.seq.end, t.val);

    if (r !=t.res) {

      cout << "отказ: тест " << t.label

           << "binary_search: "

           << t.seq.size << "элементов, val==" << t.val

           << " –> " << t.res << '\n';

      ++error_count;

    }

  }

  return error_count;

}

int main

{

  int errors = test_all(ifstream ("my_test.txt");

  cout << "Количество ошибок: " << errors << "\n";

}

Вот как выглядят некоторые тестовые данные.

{ 1.1 1 { 1 2 3 5 8 13 21 } 1 }

{ 1.2 5 { 1 2 3 5 8 13 21 } 1 }

{ 1.3 8 { 1 2 3 5 8 13 21 } 1 }

{ 1.4 21 { 1 2 3 5 8 13 21 } 1 }

{ 1.5 –7 { 1 2 3 5 8 13 21 } 0 }

{ 1.6 4 { 1 2 3 5 8 13 21 } 0 }

{ 1.7 22 { 1 2 3 5 8 13 21 } 0 }

{ 2 1 { } 0 }

{ 3.1 1 { 1 } 1 }

{ 3.2 0 { 1 } 0 }

{ 3.3 2 { 1 } 0 }

Здесь видно, почему мы использовали строковую метку, а не число: это позволяет более гибко нумеровать тесты с помощью десятичной точки, обозначающей разные тесты для одной и той же последовательности. Более сложный формат тестов позволяет исключить необходимость повторения одной и той же тестовой последовательности в файле данных.

<p id="AutBody_Root516"><strong>26.3.2.3. Случайные последовательности</strong></p>

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

Существует один прием, который иногда помогает решить эту проблему: просто сгенерировать много случайных значений. Например, ниже приведена функция, которая записывает описание теста в поток cout с помощью функции randint из раздела 24.7 и заголовочного файла std_lib.facilities.h.

void make_test(const string& lab,int n,int base,int spread)

 // записывает описание теста с меткой lab в поток cout

 // генерирует последовательность из n элементов, начиная

 // с позиции base

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