Тестовые оснастки
Как в любом тестовом методе, тестовой оснастке для проверки результатов операций нужно знать правильные ответы. Важнейшим является способ, использованный нами для тестирования memset, – создание простейшей версии тестируемой функции и сравнение ее результатов с результатами основных тестов. Это можно осуществлять в несколько этапов, как будет показано в следующем примере.
Один из авторов некогда создавал библиотеку для работы с растровой графикой; среди прочих в этой библиотеке существовал оператор для копирования блоков пикселей из одного изображения в другое. В зависимости от параметров эта операция осуществлялась как простое копирование памяти, или требовала преобразования пикселей из одного цветового пространства в другое, или выполняла мозаичное размещение введенного образца в прямоугольной области, или использовала комбинацию этих и некоторых других способов.
Спецификация оператора выглядела просто, реализация же его требовала написания большого количества специфического кода для обработки всех возможных случаев. Для того чтобы убедиться в правильности всего кода, требовалась хорошая стратегия тестирования.
Сначала вручную был написан простейший кож, осуществляющий корректные операции для одного пикселя, который использовался для тестирования работы функций библиотеки с одним пикселем. Завершив этот этап, можно было быть уверенным в том, что для случая одного пикселя оператор библиотеки работает корректно.
Далее был написан код, использующий эту отлаженную однопиксельную обработку, – получился прообраз (медленный и неудобный, но это неважно) оператора, работающего с одной горизонтальной строкой пикселей; с этим прообразом и производилось сравнение библиотечной обработки строк. По окончании данного этапа библиотека была проверена на обработку строк пикселей.
Далее так же последовательно строки использовались для обработки прямоугольных областей, те в свою очередь – для создания мозаик и т. д. По ходу дела было обнаружено немало ошибок, в том числе и в тестовой программе, но это-то как раз и является одним из преимуществ методики: тестируются две независимые версии, при этом обе – корректируются. Если какой-то тест не проходит, в первую очередь распечатываются результаты работы тестирующей программы, и на их основе выясняется место возникновения ошибки и проверяется корректность самой тестирующей версии.
Библиотека изменялась и переписывалась под разные платформы много лет, и тестирующая версия не раз оказывалась незаменимым инструментом для поиска ошибок.
При использованном поэтапном подходе тестирующая программа должна была запускаться каждый раз заново для проверки уверенности в работе библиотеки. Кстати, в данном случае тесты были не замкнутыми, а скорее вероятностными: тестовые задания генерировались случайным образом, и при достаточно большом количестве запусков можно было с хорошей вероятностью считать, что все возможные варианты (и, стало быть, все ветви кода) оказались проверенными.
При большом количестве вариантов тестовых случаев такая стратегия более удачна, чем создание наборов тестов вручную, и гораздо более эффективна, чем замкнутое тестирование.
Упражнение 6.6
Создайте, основываясь на описанных нами приемах, тестовую оснастку для memset.
Упражнение 6.7
Создайте тесты для остальных функций семейства mem….
Упражнение 6.8
Определите режим тестирования для числовых методов типа sqrt, sin и им подобных из библиотеки math.h. Какие вводимые значения имеют смысл? Какие независимые проверки могут быть осуществлены?
Упражнение 6.9
Определите механизмы для тестирования функций С семейства stг… (например, strcmp). Некоторые из этих функций, особенно те, что служат для разбиения на лексемы – типа strtok или strcspn, значительно сложнее, чем функции семейства mem…, и, следовательно, для их проверки потребуются более изощренные тесты.