Тестирование программы
Тестирование программы — это проверка её работы на специально выбранных наборах входных данных. Цель тестирования — убедиться, что программа выдаёт правильный результат не только на обычных, но и на крайних и особых случаях.
Что такое тестирование
Тестом называют набор входных данных вместе с ожидаемым результатом работы программы. Программу запускают на тесте, сравнивают полученный ответ с ожидаемым и делают вывод о правильности решения. Если ответы различаются, найдено несоответствие: в программе может быть ошибка, неверно понято условие или неправильно составлен сам тест.
Тестирование программы — выполнение программы на выбранных входных данных для проверки соответствия результата условию задачи. Тестовый набор включает входные данные и правильный ответ, с которым сравнивают результат.
Тестирование не доказывает абсолютную правильность программы: невозможно проверить бесконечное количество входов. Однако грамотно составленный набор тестов позволяет обнаружить большинство типичных ошибок. После обнаружения ошибки применяют отладку, то есть ищут место и причину неправильного поведения.
Перед тестированием полезно разобраться, как программа выполняется шаг за шагом. Для этого применяют выполнение программы и трассировку программы. Если ошибка связана не с записью команды, а с неверной идеей решения, это логическая ошибка.
Как подбирать тесты
Тесты должны проверять разные варианты поведения программы. Нельзя ограничиваться одним обычным примером: программа может правильно работать на нём случайно или потому, что ошибка проявляется только при другом расположении данных.
- Проверить обычный случай из середины допустимого диапазона.
- Проверить минимально допустимые значения входных данных.
- Проверить максимально допустимые значения.
- Проверить значения около границ: на единицу меньше и на единицу больше, если они допустимы.
- Проверить особые случаи: ноль, единицу, равные элементы, пустой результат, отрицательные значения или отсутствие решения.
- Проверить несколько вариантов порядка данных, если порядок может влиять на алгоритм.
Если в условии сказано, что \(1 \le n \le 100\), полезны тесты \(n=1\), \(n=2\), \(n=99\) и \(n=100\). Значения \(n=0\) и \(n=101\) являются недопустимыми, но их можно рассматривать при отдельной проверке устойчивости программы, если это разрешено условием.
Набор тестов должен покрывать все ветви оператора ветвления, разные варианты циклов и существенные границы входных данных. Если условие разбито на случаи, хотя бы один тест должен проверять каждый случай.
Для задач с условиями важно отдельно проверить равенство границе. Ошибка часто возникает, когда вместо \(x \le a\) в программе записывают \(x < a\) или наоборот. В задачах с циклами нужно проверить, выполнится ли цикл ноль раз, один раз и несколько раз.
Какой набор лучше всего проверяет условие \(10 \le x \le 20\)?
Граничные и особые случаи
Граничные значения — это минимальные и максимальные допустимые значения параметров, а также значения непосредственно рядом с ними. Именно на границах часто обнаруживаются ошибки на единицу, неправильные знаки сравнения и неверное количество повторений цикла.
Формула (1) показывает типичный набор целых значений для проверки отрезка. Если диапазон очень мал, одинаковые значения не нужно повторять. Если вход вещественный, вместо соседних целых выбирают значения чуть меньше и чуть больше границы, например \(a-0{,}1\), \(a\) и \(a+0{,}1\).
Особые случаи зависят от задачи. Для массива важны один элемент, все равные элементы, уже отсортированный и обратно отсортированный массив. Для строки — пустая строка, один символ, одинаковые символы и отсутствие искомого символа. Для деления необходимо проверить, не становится ли знаменатель равным нулю.
Разделите входные данные на группы, внутри которых программа должна вести себя одинаково: например, отрицательные, нулевые и положительные числа. Выберите хотя бы один представитель каждой группы, а границы групп проверьте отдельно.
Разобранный пример: поиск ошибки в цикле
Рассмотрим программу, которая должна вычислить сумму целых чисел от \(1\) до \(n\). В условии \(1 \le n \le 100\). Проверим фрагмент на Python.
1n = int(input()) 2s = 0 3for i in range(1, n): 4 s += i 5print(s)
На первый взгляд цикл складывает числа от \(1\) до \(n\). Но в Python правая граница функции range не включается. Поэтому фактически складываются числа от \(1\) до \(n-1\). Подберём тесты и сравним ответы с формулой суммы.
Минимальный тест \(n=1\) сразу обнаруживает ошибку: программа печатает \(0\) вместо \(1\). Исправленный цикл должен иметь вид for i in range(1, n + 1). После исправления нужно повторить тестирование, включая \(n=1\), обычное значение и \(n=100\).
Проверка программы по структуре
При анализе программы полезно составить список её частей: ввод данных, вычисления, условия, циклы и вывод. Для каждой ветви нужно найти тест, который приводит выполнение именно в неё. Для каждого цикла нужно выяснить начальное значение счётчика, условие продолжения и момент остановки.
Управляющие конструкции программы определяют возможные пути выполнения. В сложных решениях отдельно проверяют вызов функции или вызов процедуры, особенно если они получают граничные параметры. При рекурсивном алгоритме важно понимать стек вызовов и проверить самый простой случай остановки.
| Часть программы | Что проверить | Типичный тест |
|---|---|---|
| Ввод | Диапазон и формат данных | Минимум и максимум |
| Ветвление | Каждое условие и равенство границе | Значения до, на и после границы |
| Цикл | Ноль, один и несколько проходов | Пустой или минимальный набор |
| Массив или строка | Размер и особый порядок элементов | Один элемент, одинаковые элементы |
| Вывод | Формат и величину ответа | Тест с заранее известным результатом |
1. Проверять только пример из условия. 2. Не проверять минимальное значение. 3. Забывать случай \(0\) или пустой набор. 4. Считать, что правильный ответ на одном тесте доказывает правильность программы. 5. Сравнивать ответы «на глаз», не вычисляя эталон. 6. После исправления проверять только найденный тест и не запускать остальные.
Эталонный результат и повторная проверка
Эталонный результат можно получить вручную, по математической формуле, с помощью простой независимой программы или прямым перечислением для маленьких входов. Проверяющая программа не должна повторять ту же ошибочную идею: иначе одинаковая ошибка может скрыть ошибку решения.
После изменения кода проводят повторное тестирование. Сначала повторяют тест, на котором обнаружили ошибку, затем проверяют соседние значения и весь заранее подготовленный набор. Такой набор называют регрессионным: он помогает убедиться, что исправление одной части не сломало другую.
Хороший тест отвечает на вопрос: какое конкретное правило или случай он проверяет? Если назначение теста нельзя объяснить, скорее всего, набор составлен случайно.
Проверь себя
Главное
- Тест — это входные данные и ожидаемый результат; тестирование сравнивает фактический и правильный ответы.
- Проверяйте обычные, граничные и особые случаи, а также каждую ветвь и важные режимы циклов.
- Для границ полезны значения на границе, рядом с ней и внутри диапазона.
- Ошибка на единицу часто возникает из-за неправильного знака сравнения или невключённой границы цикла.
- После исправления обязательно повторяйте ошибочный тест и весь набор существенных тестов.