Все более или менее сложные программные системы содержат ошибки (если не свои, то вызванные используемыми библиотеками или из-за неточного осознания поведенческих парадигм используемых фреймворков).
Модульные тесты часто используются для тестирования системы на этапе разработки.
Таким образом, программист может контролировать поведение системы в контрольных точках и граничных значениях.
Зачастую именно неправильная обработка граничных значений приводит к проблемам.
И опытные программисты это знают и учитывают при разработке Unit-тестов.
Удобство Unit-тестов еще и в том, что при изменении кода вы рассчитываете получить предсказуемые результаты и проводите полностью автоматическое тестирование по существующим сценариям, чтобы быстро выявить проблемы, вызванные изменениями.
Например, вы пишете код для работы на Intel и PPC, разрабатываете его на Intel, но учитываете порядок байтов.
Потом запускаешь свои Unit-тесты, сравниваешь выходные данные с эталоном и находишь несоответствия — видно, что забыл где-то байты перевернуть — поправляешь — все в порядке.
Однако любой пользователь всегда несет в себе элемент случайности.
Опытный программист сочетает в себе талант качественного тестировщика и может отловить множество ошибок еще до выхода программы.
Если программа делает больше, чем просто печатает «Hello World!», то скрытые ошибки в любом случае остаются.
Это могут быть и логические ошибки.
Программа компилируется, все Предупреждения удаляются.
но иногда что-то идёт не так.
для пользователя (который живёт далеко в доме на острове в Тихом океане - нет возможности подойти к нему и потрогать) .
Программист позвонил и протестировал со своей стороны всё, что мог, но ошибок не обнаружил.
Что делать? Любое приложение можно рассматривать как массив взаимосвязанных компонентов C, объединенных в логическую сеть.
Каждый компонент принимает аргументы I в качестве входных данных и выдает результаты O в качестве выходных данных.
Составляем генераторы для получения случайных аргументов I, подаем их на вход компонентам C и проверяем выходы O, а также проверяем целостность состояния компонента C дополнительными тестами.
Поэтому мы тестируем каждый компонент со случайным набором данных.
Этот метод можно распространить на всю сеть, состоящую из компонентов, или на выбранные подсети.
При таком стресс-тестировании случайные данные обязательно найдут определенные скрытые граничные точки и проверят те ветви ваших условий, которых ваши вымышленные тесты не достигли бы при обычных обстоятельствах.
Мы запускаем тысячи итераций все большего и большего количества случайных данных, отбираем и запрашиваем случайные (валидные) операции над этими данными у компонентов.
Конечно, на 1 этапе вам придется настроить подсистему контроля целостности, но дальше все пойдет как по маслу.
При обнаружении сбоя система вероятностного тестирования сохраняет журнал команд в файл, так что вместо фактора случайности мы получаем вполне конкретный сценарий, который приводит к ошибке и может быть воспроизведен.
Прежде чем искать ошибку, можно попробовать сократить этот скрипт до минимальной длины, чтобы ошибка все равно появлялась.
После устранения ошибки этот скрипт должен работать без сбоев, а в нашем тестовом наборе на будущее есть еще один Unit-тест. В качестве практического примера можно использовать систему вероятностного тестирования для выявления ошибок при создании словарей.
Можно выделить следующие операции:
- Добавить слово
- Придать существующему слову значение
- Удалить слово
- Проверьте целостность
srand(time(0)); NSMutableString * log = [NSMutableString string];// for commands int prev = -1; unsigned i; #define ST_COUNT 2000 id model = [SomeModelFactory createModelObject]; for (i = 0; !status && i < ST_COUNT; i++) { int todo; do { todo = rand() % 4; } while (3 == todo && todo == prev); prev = todo; if (i + 1 == ST_COUNT)// last iter. todo = 3;// force int. check switch (todo) { case 0:// add new word to the model { … } case 1:// set existing word { … } case 2:// remove word { … } case 3:// pint. check { if (i + 1 == ST_COUNT || rand() % 2) { … status = 3;// set some error code if fail } } } } if (status) { [log writeToFile:@"/tmp/commands.log" atomically:YES encoding:NSUTF8StringEncoding error:NULL ]; exit(status); }Генератор аргументов:
char genChar() { // allowed chars static char allowed[] = "ABCDEFGHIJKLMNOPQRSTUVWXUZabcdefghijklmnopqrstuvwxyz1234567890/"; return allowed[rand() % (sizeof(allowed)-1)]; } NSString* genWord(int min, int max) { NSMutableString * res = [NSMutableString string]; if (max < min) max = min; int toGen = min + rand() % (max - min + 1); int i; for (i = 0; i < toGen; i++) [res appendFormat:@"%c",genChar()]; return res; }Все логи с ошибками можно переместить из /tmp, например, в папку Issues, в папки Case-1, Case-2,.
Использовать номер дела для проведения любой проверки в будущем.
Вероятность выбора тех или иных операций (из перечисленных выше пунктов) можно регулировать.
Таким образом, проверку целостности можно не выполнять каждую итерацию, и в случае сбоя можно более точно определить местоположение, выполняя проверку после каждой операции.
При проведении операций мы самостоятельно сохраняем состояние системы в обычном словаре NSMutableDictionary, а при проверке целостности проверяем, что количество слов в контрольном и созданном словарях одинаково, что все слова найдены и имеют одинаковый текст статьи как в тестовом примере.
Что слово можно получить по индексу, и то же самое слово можно получить по индексу.
Чтобы повлиять на систему проверки, вы можете изменить выходные данные генераторов аргументов, создав больше пересечений.
и тем самым сместив вероятность возникновения определенных событий.
В приведенном выше примере мы можем генерировать слова от 1 до 10 букв и статьи от 1 до 100 символов.
Мы можем изменить условия и генерировать слова из 1-3 букв и артикли из 1-10 символов, поэтому мы можем попасть в вероятностное поле других ошибок.
Мы также можем влиять на вероятности выбора доступных операций и либо резко увеличивать, либо резко сжимать словарь.
Мы даже можем менять политику вероятностей выбора также случайным образом, как ветер, меняющий свое направление.
Фактически только благодаря методу вероятностного тестирования в нашем проекте мы поймали 5 скрытых и очень сложных ошибок в уже протестированном двигателе, в котором не было видимых намеков на неисправность! Вероятностное тестирование может на шаг приблизить нас к моделированию конечного пользователя и помочь обнаружить скрытые дефекты.
Теги: #юнит-тестирование #Cocoa #objective-c #тестирование ИТ-систем
-
Все О Lenovo G550
19 Oct, 24 -
О Велосипедах И Рамах
19 Oct, 24 -
Статьи Википедии Будут Проверены
19 Oct, 24 -
История Voip И Интернет-Телефонии
19 Oct, 24