Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:testing [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== testing ======
 +<​sub>​{{testing.odt|Original file}}</​sub>​
 +
 +===== тестирование программного обеспечения =====
 +
 +крис касперски ака мыщъх, no-email
 +
 +Before you can debug, you need bugs!
 +
 +основной принцип тестирования
 +
 +**программные ошибки коварны и злы. сколько они загубили хороших проектов! сколько времени было угрохано на поиски плавающих багов, затаившихся в засаде и выскакивающих во время демонстрации продукта заказчику. но программист хитрее и терпеливее. вооруженный современным инструментарием диагностики он врезается в самое гнездилище багов и бьет их наповал.**
 +
 +===== введение =====
 +
 +В среднем тестирование отнимает 50% времени и 50% стоимости от общей сметы проекта (обязательно учитывайте это, закладывая бюджет). В больших компаниях (Intel, IBM, Microsoft) за каждым разработчиком закреплен личный тестировщик. Прошло то время, когда эту работу выполнял второсортный программист,​ которого еще не подпускали к самостоятельному кодированию (мол, прежде чем допускать свои ошибки,​ сначала пусть учатся на чужих). Сегодня тестировщик – это высококвалифицированный и хорошо оплачиваемый специалист,​ в услугах которого нуждаются тысячи фирм и который никогда не сидит без работы.
 +
 +Когда вам скажут,​ что жизненный цикл продукта состоит из проектирования,​ реализации,​ тестирования и поддержки,​ не верьте! Тестирование сопровождает проект всю его жизнь — от момента рождения до самой смерти. Проектировщик закладывает механизмы самодиагностики и вывода "​телеметрической"​ информации. Разработчик тестирует каждую запрограммированную им функцию (тестирование на микро-уровне). Бета-тестеры проверяют работоспособность всего продукта в целом. У каждого из них должен быть четкий план действий,​ в противном случае тестирование провалится еще не начавшись.
 +
 +{{testing_Image_0.jpg}}
 +
 +Рисунок 1 программист не может полагаться на свои чувства
 +
 +===== тестирование на микро-уровне =====
 +
 +В идеале для каждой функции исходного кода разрабатывается набор автоматизированных тестов,​ предназначенных для проверки ее работоспособности. Лучше всего поручить эту работу отдельной группе программистов,​ поставив перед ними задачу:​ разработать такой пример на котором функция провалится. Вот, например,​ функция сортировки. Простейший тест выглядит так. Генерируем произвольные данные,​ прогоняем через нее и, если для каждого элемента N условие N <​= N+1 (N >​= N+1 для сортировки по убыванию) истинно,​ считаем,​ что тест пройдет правильно. Но ведь этот тест неправильный! Необходимо убедиться,​ что на выходе функции присутствуют все в исходные данные и нет ничего лишнего! Многие функции нормально сортируют десять или даже тысячу элементов,​ но спотыкаются на одном или двух (обычно это происходит при сортировке методом деления напополам). А если будет ноль сортируемых элементов?​ А если одна из вызываемых функций (например,​ malloc), возвратит ошибку — сможет ли тестируемая функция корректно ее обработать?​ Сколько времени (системных ресурсов) потребуется на сортировку максимально возможного числа элементов?​ Неоправданно низкая производительность — тоже ошибка!
 +
 +Существует два основных подхода к тестированию — черный и белый ящики. "​Черный ящик"​ – это функция с закрытым кодом, проверка которого сводится к тупому перебору всех комбинаций аргументов. Очевидно,​ что подавляющее большинство функций не могут быть протестированы за разумное время (количество комбинаций слишком велико). Код белого ящика известен и тестировщик может сосредоточить свое внимание на пограничных областях. Допустим,​ в функции есть ограничение на предельно допустимую длину строки в MAX_LEN символов. Тогда, следует тщательно исследовать строки в MAX_LEN –1,​ MAX_LEN и MAX_LEN+1 символов,​ поскольку ошибка "в плюс минус один байт"​ — одна из самых популярных.
 +
 +Тест должен задействовать все ветви программы,​ чтобы после его выполнения не осталось ни одной незадействованной строчки кода. Соотношение кода, который хотя бы рад получил выполнение,​ к общему коду программы,​ называется покрытием (coverage) и для его измерения придумано множество инструментов — от профилировщиков,​ входящих в штатный комплект поставки компиляторов,​ до самостоятельных пакетов,​ лучим из которых является NuMegaTrueCoverage.
 +
 +Разработка тестовых примеров — серьезная инженерная задача,​ зачастую даже более сложная,​ чем разработка самой "​подопытной"​ функции. Неудивительно,​ что в реальной жизни к ней прибегают лишь в наиболее ответственных случаях. Функции с простой логикой тестируются "​визуально"​. Вот потому у нас все глючит и падает.
 +
 +Всегда транслируйте программу с максимальным уровнем предупреждений (для MicrosoftVisualC++ это ключ /W4), обращая внимание на все сообщения компилятора. Некоторые,​ наиболее очевидные ошибки обнаруживаются уже на этом этапе. Сторонние верификаторы кода (lint, smatch) еще мощнее и распознают ошибки,​ с которыми трансляторы уже не справляются.
 +
 +Тестирование на микро-уровне можно считать законченным тогда, когда функция компилируется несколькими компиляторами и работает под всеми операционными системами,​ для которых она предназначена.
 +
 +{{testing_Image_1.jpg}}
 +
 +Рисунок 2 никто не застрахован от неприятностей…
 +
 +===== регистрация ошибок =====
 +
 +Завалить программу — проще всего. Зафиксировать обстоятельства сбоя намного сложнее. Типичная ситуация:​ тестировщик прогоняет программу через серию тестов. Не пройденные тесты отправляются разработчику,​ чтобы тот локализовал ошибку и исправил баги. Но у разработчика эти же самые тесты проходят успешно! А… он уже все переделал,​ перекомпилировал с другими ключами и т. д. Чтобы этого не происходило используйте системы управления версиями — Microsoft Source Safe или юниховый CVS.
 +
 +Сначала тестируется отладочный вариант программы,​ а затем точно так же финальный. Оптимизация — коварная штука и дефекты могут появится в самых неожиданных местах,​ особенно при работе с вещественной арифметикой. Иногда в этом виноват транслятор,​ но гораздо чаще — сам программист.
 +
 +Самыми коварными являются "​плавающие"​ ошибки,​ проявляющиеся с той или иной степенью вероятности — девятьсот прогонов программа проходит нормально,​ а затем неожиданно падает без всяких видимых причин. Эй, кто там орет, что такого не бывает?​ Машина,​ дескать,​ детерминирована,​ и если железо исправно,​ то баг либо есть, либо нет. Ага, разбежались! Многопоточные приложения и код, управляющий устройствами ввода/​вывода,​ порождает особый класс не воспроизводимых ошибок,​ некоторые из которых могут проявляться лишь раз в несколько лет (!). Вот типичный пример:​
 +
 +char *s;
 +
 +f1() {intx=strlen(s);​ s[x]='​*';​ s[x+1] = 0;}// поток 1
 +
 +f2() {printf("​%s\n",​s);​}//​ поток 2
 +
 +Листинг 1 пример плавающей ошибки
 +
 +Один поток модифицирует строку,​ а другой выводит ее на экран. Какое-то время программа будет работать нормально,​ пока поток 1 не прервется в тот момент,​ когда звездочка уже уничтожила завершающий символ нуля, а новый ноль еще не был дописан. Легко доказать,​ что существуют такие аппаратные конфигурации,​ на которых эта ошибка не проявится никогда (для этого достаточно взять однопроцессорную машину,​ гарантированно успевающую выполнить весь код функции f1 за один квант). По закону подлости этой машиной обычно оказывается компьютер тестировщика и у него все работает. А у пользователей — падает.
 +
 +Чтобы локализовать ошибку,​ разработчику недостаточно знать, что "​программа упала",​ необходимо сохранить и затем тщательно проанализировать ее состояние на момент обрушения. Как правило,​ для этого используется аварийный дамп памяти,​ создаваемый утилитами типа Доктора Ватсона (входит в штатный комплект поставки операционной системы) или на худой конец значение регистров процессора и содержимое стека. Поскольку не все ошибки приводят к аварийному завершению программы,​ разработчик должен заблаговременно предусмотреть возможность создания дампов самостоятельно — по нажатию специальной комбинации клавиш или при срабатывании внутренней системы контроля.
 +
 +{{testing_Image_2.jpg}}
 +
 +Рисунок 3 вот к чему приводят ошибки проектирования при загрузке системы реальными данными
 +
 +===== бета-тестирование ​ =====
 +
 +Собрав все протестированные модули воедино,​ мы получаем минимально работоспособный продукт. Если он запускается и не падает — это уже хорошо. Говорят:​ посадите за компьютер неграмотного человека,​ пусть давит на все клавиши,​ пока программа не упадет. Ну да, как же! Тестирование программы это серьезная операция и такой пионерский подход здесь неуместен. Необходимо проверить каждое действие,​ каждый пункт меню, на всех типах данных и операций. Программистом бета-тестер может и не быть, но квалификацию продвинутого пользователя иметь обязан.
 +
 +Уронив программу (или добившись от нее выдачи неверных данных),​ бета-тестер должен суметь воспроизвести сбой, т. е. выявить наиболее короткую последовательность операций,​ приводящую к ошибке. А сделать это ой как непросто! Попробуй-ка вспомнить,​ какие клавиши были нажаты! Что? Не получается?​! Су. Используйте клавиатурные шпионы. На любом хакерском сайте их валом. Пусть поработают на благо народа (не вечно же пароли похищать). Шпионить за мышью намного сложнее — приходится сохранять не только позицию курсора,​ но координаты всех окон или задействовать встроенные макросредства (по типу Visual Basic'​a в Word). В общем, мышь — это саксь и маст дай. Нормальные бета-тестеры обходятся одной клавиатурой. Полный протокол нажатий сокращает круг поиска ошибки,​ однако,​ с первого раза воспроизвести сбой удается не всегда и не всем.
 +
 +В процессе тестирования приходится многократно выполнять одни и те же операции. Это раздражает,​ ненадежно и непроизводительно. В штатную поставку Windows 3.x входил клавиатурный проигрыватель,​ позволяющий автоматизировать такие операции. Теперь же его приходится приобретать отдельно. Впрочем,​ такую утилиту можно написать и самостоятельно. В этом помогут функции FindWindow и SendMessage.
 +
 +Тестируйте программу на всей линейке операционных систем:​ Windows 98,​ Windows 2000,​ Windows 2003 и т. д. Различия между ними очень значительны. Что стабильно работает под одной осью, может падать под другой,​ особенно если она перегружена кучей конфликтующих приложений. Ладно, если это кривая программа Васи Пупкина (тут на пользователя можно и наехать),​ но если ваша программа не уживается в MS Office или другими продуктами крупных фирм, бить будут вас. Никогда не меняйте конфигурацию системы в процессе тестирования! Тогда будет трудно установить чей это баг. Хорошая штука — виртуальные машины (VM Ware, Microsoft Virtual PC). На одном компьютере можно держать множество версий операционных систем с различной комбинацией установленных приложений — от стерильной до полностью захламленной. При возникновении ошибки,​ состояние системы легко сохранить на жесткий диск, обращаясь к нему впоследствии столько раз, сколько потребуется.
 +
 +У вас программа работает,​ а у пользователя — нет. Что делать?​! Для начала — собрать информацию о конфигурации его компьютера ("​Панель управления  Администрирование  Управление компьютером  Сведения о системе"​ или утилиты MSInfo32.exe). К сожалению,​ установить виновника таким путем сходу не удастся. Может, там вирус сидит и вредительствует. Или глючит какой драйвер. Но, имея несколько отчетов от различных пользователей,​ в них можно выявить некоторую закономерность. Например,​ программа не идет на таком-то процессоре или видео-карте.
 +
 +Другая возможная причина — утечка ресурсов. Утечки возникают всякий раз, когда программа злостно не освобождает то, что постоянно запрашивает. Чаще всего приходится сталкиваться с утечками памяти,​ но ничуть не хуже утекают перья, кисти, файловые дескрипторы… В общем, практически любые объекты ядра, USER и GDI. Тестировщик,​ работая с программой непродолжительные отрезки времени,​ может этого и не заметить (особенно,​ если у него стоит Windows NT/​2000/​XP в которой ресурсы практически неограниченны),​ но при "​живой"​ эксплуатации у пользователей появляются огромные проблемы. Сначала легкое замедление быстродействия системы,​ затем конкретные тормоза,​ переходящие в полный завис, и наконец резет, сопровождаемый колоритным матом.
 +
 +Отладочные библиотеки,​ входящие в состав компилятора Microsoft Visual C++ легко обнаруживают большинство утечек памяти. В сложных случаях приходится прибегать к верификаторам кода или динамическим анализаторам наподобие NuMegaBoundsChecker. Но высшей инстанцией является эксперимент. Запустите "​Диспетчер Задач Windows NT"​ и некоторое время поработайте с тестируемой программой. Вкладка "​Процессы"​ отображает текущие счетчики дескрипторов,​ размер выделенной памяти и т. д.(По умолчанию видны лишь некоторые из них, зайдите в меню Вид  Выбрать Столбцы и взведите все галочки). Если какой-то счетчик неуклонно увеличивает свое значение после некоторых операций — это утечка.
 +
 +Для исследования работоспособности программы в условиях катастрофической нехватки ресурсов (памяти,​ дискового пространства) Microsoft включила в состав Platform SDK утилиту Stress.exe, снабдив ее иконкой танцующего мамонта. Корректно спроектированное приложение должно выживать при любых обстоятельствах. Обломали с выделением памяти из кучи? Переходите на резервный источник (стек, секция данных). Освободите все ненужное,​ но любой ценой сохраните все данные! Всегда сохраняйте при старте программы минимально необходимое количество памяти "​про запас",​ а потом используйте его как НЗ. Тоже самое относится и к дисковому пространству.
 +
 +{{testing_Image_3.jpg}}
 +
 +Рисунок 4 клавиатура — основной инструмент бета-тестера
 +
 +===== вывод диагностической информации =====
 +
 +Самое страшное — когда программа неожиданно делает из обрабатываемых чисел "​винегрет"​. Совершенно непонятно кто в этом виноват и откуда надо плясать. Ошибка в одной функции может аукаться в совершенно посторонних и никак не связанных с ней местах. Удар по памяти,​ искажение глобальных переменных или флагов (со)процессора… Здесь дамп уже не помогает. Застывшая картина статичного слепка памяти не объясняет с чего началось искажение данных,​ в каком месте и в какое время оно произошло.
 +
 +Для локализации таких ошибок в программу заблаговременно внедряются "​телеметрические"​ механизмы для генерации диагностической информации. В идеале,​ следовало бы протоколировать все действия выполняемые программой,​ запоминая все машинные команды в специальном буфере. Собственно говоря,​ soft-ice в режиме обратной трассировки (backtrace) именно так и поступает,​ позволяя нам прокручивать программу задом наперед. Это чудовищно упрощает отладку,​ но… как же оно тормозит! Искусство диагностики как раз и состоит в том, чтобы отобрать минимум важнейших параметров,​ фиксирующих максимум происходящих событий. По крайней мере отмечайте последовательность выполняемых функций вместе с аргументами.
 +
 +Чаще всего для этой цели используется тривиальный fprintf для записи в файл или syslog для записи в системный журнал (в Windows NT это осуществляется посредством вызова API-функции ReportEven, экспортируемой библиотекой ADVAPI32.DLL). Начинающие допускают грубую ошибку,​ включая диагностику только в отладочную версию и удаляя ее из финальной:​
 +
 +#ifdef _DEBUG_
 +
 +fprintf(flog,​ "%s:%d a = %08Xh; b = %08Xh\n",​__FILE__,​__LINE__,​a,​b);​
 +
 +#endif
 +
 +Листинг 2 никогда так не поступайте!
 +
 +Помните Пруткова:​ зачем тебе солнце,​ когда днем и без него светло?​ Когда такая программа упадет у пользователя,​ в руках программиста не окажется никакой диагностической информации,​ дающей хоть какую-то зацепку. Лучше поступать так:
 +
 +
 +
 +if (_DEBUG_) fprintf(flog,​ "%s:%d a = %08Xh; b = %08Xh\n",​__FILE__,​__LINE__,​a,​b);​
 +
 +Листинг 3 так поступать можно, но не нужно
 +
 +Если сбой повторяется регулярно,​ пользователь сможет взвести флажок DEBUG в настройках программы и в следующий раз когда она упадет,​ передаст программисту диагностический протокол,​ если конечно не переметнется к конкурентам. Штука.
 +
 +Правильный вариант выглядит так:
 +
 +if (2*2 == 4) fprintf(flog,​ "​%s:​%da = %08Xh; b = %08Xh\n",​__FILE__,​__LINE__,​a,​b);​
 +
 +Листинг 4 так поступать можно и нужно ​
 +
 +Грамотно отобранная телеметрическая информация отнимает совсем немного места и должна протоколироваться _//​всегда_//​ (естественно,​ за размером log-файла необходимо тщательно следить,​ лучше всего, если он будет организован по принципу кольцевого буфера). Некоторые программисты используют функцию OutputDebugString,​ посылающую отладочную информацию на отладчик. В его отсутствии можно воспользоваться утилитой Марка Руссиновча DebugView или аналогичной ей. Впрочем,​ пользы от такого решения все равно немного. Это тот же логинг,​ включаемый по требованию,​ а логинг должен быть включен всегда!
 +
 +Основной недостаток fprintf в том, что аварийном завершении программы часть телеметрической информации необратимо теряется (буфера ведь остались не сброшенными). Если же их сбрасывать постоянно,​ скорость выполнения программы ощутимо замедляется. Записывайте телеметрию в разделяемую область памяти,​ а при возникновении сбоя сохраняйте в файл протокола из параллельного процесса. Так будут и волки сыты, и овцы целы.
 +
 +===== заключение =====
 +
 +Поиск ошибок не прекращается никогда! Даже когда продукт умирает,​ какие-то его компоненты используются в следующих версиях и в них вылезают новые баги. Ошибки так же неисчерпаемы как и атом! Они образуют толстый слой многолетних наслоений,​ который руки чешутся переписать,​ но начальство строго-настрого запрещает трогать. Это можно сравнить с притиркой механизма. По мере своего взросления,​ модули все дальше и дальше уходят от первоначальных спецификаций. Теперь,​ чтобы написать совместимую функцию,​ необходимо тщательно проанализировать исходный код "​старушки",​ постоянно ломая голову над вопросами:​ "​это баг или так задумано"?​ Основное правило разработчика гласит – не трогай того, что и так работает.
 +
 +Забавно,​ но многие фирмы предпочитают "​документировать"​ ошибки,​ экономя на их исправлении. В Базе Знаний или руководстве пользователя авторитетно заявляется:​ "​туда ходить не надо, кто не послушался — сам виноват"​. Возможности,​ которые так и не удалось отладить,​ но которые нельзя заблокировать или изъять,​ просто не документируются. Все ими пользуются (еще бы! самая "​вкусность"​ продукта сосредоточенна именно здесь),​ у всех все падает,​ но никто не может предъявить претензию — ведь никому ничего и не обещали.
 +
 +Так что тестирование программного обеспечения это не только инженерия,​ но еще политика и маркетинг. Выживает не тот, чей продукт лучше, а тот, кто правильно его "​позиционирует"​. В конечном счете, любую ошибку можно превратить в достоинство.
 +
 +===== >>>​ врезка верификаторы кода языков Си/Си++ =====
 +
 +Самый простой верификатор — это утилита lint, входящая в штатный комплект поставки большинства юнихов. Ее возможности сильно ограничены,​ а версия для Windows распространяется только на коммерческой основе.
 +
 +Достойная альтернатива lint'​у — открытый проект **CLINT**, распространяющая в исходных текстах,​ которые можно скачать с сервера сообщества "​кузницы":​ http://​sourceforge.net/​projects/​clint/​.
 +
 +Еще мощнее **SPLINT**, нацеленный на автоматизированный поиск переполняющихся буферов и прочих программистских ошибок,​ которые не находят lint и CLINT. Это серьезный,​ хорошо документированный продукт,​ распространяющийся в исходных текстах на некоммерческой основе уже скомпилированный под Windows, Linux, Solaris и FreeBSD (CLINTпоставляется только в исходных текстах,​ с которыми еще предстоит повозится). http://​lclint.cs.virginia.edu/​
 +
 +**Smatch****C**sourcechecker представляет собой автоматический анализатор исходного кода для нахождения типовых ошибок (утечек памяти,​ переполнений буфера,​ паразитных NULL указателей и т. д.), созданный в рамках проекта по выявлению ошибок в Linux-ядре. Распространяется в виде ​ патчей к gcc-комилятору и набора perl-скриптов для анализа дампов. http://​smatch.sourceforge.net/​
 +
 +Совершенно иной подход исповедует **MLC**, он же Meta-LevelCompilation (компилятор мета-уровня),​ транслирующий программу в промежуточный код и за счет доступа к абстрактному синтаксическому дереву,​ обнаруживающий трудноуловимые ошибки,​ пропущенные остальными верификаторами. Разработчики утверждают,​ что с помощью метакомпилятора им удалось выявить свыше 500 ошибок в реально существующих системах таких как Linux, Open BSD, Xok, Stanford FLASH и др. В настоящее время, MLC распространяется в виде бесплатного компилятора xgcc, базирующегося на GNU C, и вспомогательного транслятора metal для создания расширений. http://​metacomp.stanford.edu/​
 +
 +{{testing_Image_4.jpg}}
 +
 +Рисунок 5 багов надо удалять пока они маленькие
 +
 +===== >>>>​ врезка демонстрация ошибок накопления =====
 +
 +С вещественной арифметикой следует обращаться очень осторожно. Рассмотрим следующий пример:​
 +
 +int a; float x; x = 0;
 +
 +for (a = 0;a < 10; a++) x+=0.7;
 +
 +printf("​%f\n",​x);​
 +
 +Листинг 5 что здесь неправильно?​
 +
 +Попробуйте угадать,​ что получится в результате?​ Десять раз по 0,7 будет… 7. Щас! Разбежались! Этого нам никто не гарантировал! Машинное представление числа 0,7 не является точной дробью и ближе к 0.6999… Многократные сложения приводят к накоплению погрешности вычислений и программа,​ откомпилированная компилятором Microsoft Visual C++ 6.0 с настойками по умолчанию дает 6.999999. Оптимизированный вариант (ключ /Ox) возвращает правильный результат — 7.000000. В чем же дело?
 +
 +Заглянем в дизассемблерный код:
 +
 +|не оптимизированный вариант|оптимизированный вариант|
 +|moveax, [a]\\ ; грузим в регистр EAX переменную a\\ addeax, 1\\ ; увеличиваем EAX на единицу\\ mov[a], eax\\ ; обновляем содержимое переменной a\\ loc_1F:​cmp[a],​ 0Ah\\ ; сравниваем переменную a с 10\\ jgeshort loc_33\\ ; if (a >= 10) goto loc_33\\ fld[x]\\ ; затаиваем b на вершину стека сопра\\ faddds:​__real@8@3ffeb3333333333\\ ; складываем содержимое вершины с 0.7\\ fstp[x]\\ ; выталкиваем полученный результат в x\\ jmpshort loc_16\\ ; мотаем цикл\\ loc_33:​|fldds:​__real@4@000000000000000\\ ; заталкиваем 0.7 на вершину стека сопра\\ moveax, 0Ah\\ ; загружаем в регистр EAX число 10\\ loc_B:​faddds:​__real@8@3ffeb33333333\\ ; складываем содержимое вершины с 0.7\\ deceax\\ ; уменьшаем EAX на единицу\\ jnzshort loc_B\\ ; if (eax !=0 ) goto loc_B\\ fstp[x]\\ ; выталкиваем содержимое вершины в x\\ |
 +
 +Таблица 1 дизассемблерный код рассматривамого примера с комментариями
 +
 +Ага! Не оптимизированный вариант выполнив сложение вещественного числа 0.7 с переменной x, каждый раз выгружает в нее текущее значение вычислений,​ что и приводит к накоплению погрешности. Оптимизированный вариант загружает переменную x типа float на вершину стека сопроцессора (где она благополучно преобразуется в double), десять раз складывает ее с 0.7, выталкивая полученный результат в x, когда все расчеты уже закончены. Отсюда и разность в поведении оптимизированной и не оптимизированной версий программы.
 +
 +А теперь представим,​ что переменная x используется как счетчик цикла. Допустим,​ программист ошибся и вместо x < 70 написал x < 69. Одна ошибка компенсируется другой и программа будет работать правильно! Но… стоит исправить одну из них, как все развалится. Потому-то программисты и не любят править отлаженный код, который работает,​ хотя на первый взгляд и не должен. Основное правило тестировщика гласит:​ исправление одной известной ошибки,​ приводит к появлению десяти новых, еще неизвестных. Отсюда:​ чем больше ошибок мы исправляем,​ тем их больше становится. Это хорошо согласуется с графиком,​ зависимости количества обнаруженных ошибок от времени тестирования,​ приведенном в книге "​Traditional Software Testing is a Failure!"​ Линды Шафер (Linda Shafer)
 +
 +{{testing_Image_5.png}}
 +
 +Рисунок 6 сначала скорость обнаружения ошибок нарастает,​ а затем, достигнув насыщения,​ сходит на нет
 +
 +===== >>>>​ врезка наиболее часто встречающие ошибки =====
 +
 +DmitryLyokhin
 +
 +1. Ошибки в хромосомах окружающих
 +
 +1.1Какой $%# это все спроектировал!?​\\ (ах, это было 8 лет назад и тот человек уже 5 лет как уволился);​
 +
 +1.2 Какому $%$ доверили расширять то, что было спроектировано ?!\\ (никто не знает, и вообще их было трое, сейчас работают в других тимах);​
 +
 +1.3 Какой $%$# это кодировал !?\\ (вот он, сидит рядом, сейчас Технический Архитектор,​ рисует квадратики и протирает штаны на митингах,​ в то время он просто учился программировать);​
 +
 +1.4 Что за #!#!#%!? Вчера все работало!"​\\ (трое $%# из соседних тимов решили что-то глобально поменять в поведении системы);​
 +
 +1.5 Какой #$#% это планировал !? Ах, это надо было вчера ?\\ (восклицание риторическое)
 +
 +2. Ошибки компилятора
 +
 +2.1 Что за #$% заставляет с завтрашнего дня использовать глюкавый компилятор фирмы M !?\\ (Ах, это глобальное политическое решение на уровне CFO);
 +
 +3. Ошибки дебаггера
 +
 +3.1 Что, нет on-target debugger и тот глючит,​ !#^%!?\\ (из средств отладки доступен только логгинг и тот тоже глючит);​
 +
 +4. Ошибки Integration
 +
 +4.1 Что за $$%?%!? Вчера все работало !\\ (9 чудаков из 4 соседних тимов зачем-то решили съинтегрировать все вместе в твой проектный бранч, причем независимо друг от друга);​
 +
 +4.2 $$%?"​%!!,​ не бейте меня, я больше не буду\\ (Это ты решил съинтегрировать свои изменения);​
 +
 +5. Просто ошибки природы
 +
 +5.1 Какой ?$?# размножил 20 строчек 40 раз в 30 файлах,​ раз забыв точку с запятой и скобку!?​\\ (Ага, это был индюк-аутсорсер,​ он программирует излюбленным индийским методом cut-n-paste,​ у него все хорошо,​ он далеко и он знает, что ты его не достанешь)
 +
 +6. Ошибки line-management'​а
 +
 +6.1 Какого #$%$% вы ко мне пристаете со своей системой учета рабочего времени!?,​ ах у меня не хватает 10 миллисекунд в рабочей неделе и ваш манагер думает,​ что я злостный прогульщик?​ Что, поправить все записи на 3 месяца назад !?
 +
 +===== >>>>​ врезка наиболее часто встречающие ошибки =====
 +
 +Nickita A Startcev
 +
 +1) Баги в ДHК сторонних разработчиков;​
 +
 +2) нецензурный кастинг;​
 +
 +3) падения OpenWatcom компилятора;​
 +
 +4) Закладывание на особенности синтаксиса конкретных компиляторов (багланд);​
 +
 +5) Закладывание на особенности таймингов конкретных компиляторов (багланд);​
 +
 +6) грязный код, не компилирующийся с -Wall -W -strict-prototypes и прочими верификаторами;​
 +
 +7) неинициализированные указатели;​
 +
 +8) логические ошибки типа сначала включить железо и только потом настроить;​
 +
 +9) Почему софтайс падает при попытке инициализации flat режима?​
 +
 +10) почему cl16 не понимает 32 разрядные регистры?​
 +
 +11) Какой идиот генерит инструкции типа DS:movax,bx ?!
 +
 +12) Какая функция и почему гадит в таблицу прерываний по адресу 00:08?
 +
 +13) Почему в качестве документации к этому чуду лежит ман на hci/1394 а не на OHCI/usb?!
 +
 +14) Какаясволочьнаписала int16 a; uint8 far* b; b=&​((int near*)a);?!
 +
 +15) Кто сказал,​ что этот блок надо выравнивать на параграф,​ а не минимум на 256 байт?!
 +
 +16) почему код из п15 таки работает на некоторых машинах?​
 +
 +===== >>>>​ Трудовые будни программиста =====
 +
 +(источник неизвестен)
 +
 +Любой русский программист после пары минут чтения кода, обязательно вскочит и произнесет обращаяськ себе: переписать это все нафиг. Потом в нем шевельнется сомнение в том, сколько времени это займет,​ и остаток дня русский программист потратит на то, что будет доказывать самому себе, что это только кажется,​ что переписать это много работы. А если взяться и посидеть немного,​ то все получится. Зато код будет красивый и правильный. На следующее утро русский программист свеж, доволенсобой и без единой запинки докладывает начальству,​ что переписать этот кусок займет один день, не больше. Да, не больше. Ну, в крайнем случае,​ два, если учесть все риски. В итоге начальство даст емунеделю и через полгода процесс будет успешно завершен. До той поры, пока этот код не увидит другой русский программист. ​
 +
 +А в это время, в соседних четырех кубиках,​ будет ни на секунду не утихать работа китайских программистов,​ непостижимым образом умудряющихся прийти раньше русского программиста,​ уйти позже, и при этом сделать примерно втрое меньше. Эта четверка,​ давно не пишет никакого кода, а только поддерживает код написанный,​ в свое время индусом и дважды переписанный двумя разными русскими. В этом коде не просто живут баги. Здесь их гнездо. Это гнездо постоянно воспроизводит себя при помощи любимой китайской технологии реиспользования кода - copy/paste. Отсюда баги расползаются в разные стороны посредством статических переменных и переменных переданных по ссылке (поскольку,​ китайский программист не может смириться с неудобствами вызванными тем, что он не может изменить значение внешней переменной переданной в его функцию модулями,​ которые переписывает русский программист).
 +
 +Вспоминая об этой функции русский программист,​ как правило на время теряет дар английской речи, и переходит к какой-то помеси русского и китайского. Он давно мечтает переписать весь кусок, над которым работают китайцы,​ но у него нет времени. На китайцах висят серьезные баги, о которых знает начальство и постоянно их торопит. Китайцы торопливо перевешивают баги друг на друга, поскольку знают, что попытки их починить приведут к появлению новых, еще худших. И в этом они правы.
 +
 +Разобраться в том, в каком порядке меняются статические переменные,​ и как приобретают своизначения,​ способен только один человек на фирме - индус. Но он пребывает в медитации. Поэтому,​ когда всю четверку уволят во время сокращения... А кого еще увольнять?​ Русский - еще не переписал свой кусок, а индус - главная ценность фирмы - он редко обращает внимание на проект,​ но когда обращает,​ все понимают,​ что так как он, архитектуру никто не знает. Так вот, когда китайцев увольняют,​ у их кода возможны две основные судьбы. Первая - он попадет к русским и его перепишут. Вторая – он попадет к местному,​ канадскому программисту. ​
 +
 +О, канадский программист это особый тип. Он ни на минуту не задумываясь,​ как рыцарь без страха и упрека,​ бросится чинить самый свирепый баг китайского кода. Этот Баг живет там уже три года, и китайцы уже четырежды (каждый по разу) сообщали начальству,​ что он починен. Но Баг каждый раз возвращался,​ как Бетмен в свой Готхем. Итак, канадский программист сделает то, чего китайцы не рисковали делать в течении трех долгих лет. Он, при помощи дебагера,​ отследит место, где статическая переменная приняла значение -1 вместо правильного 0, и решительным движением заведет рядом вторую переменную с правильным значением. Баг погибнет в неравной схватке с канадским программистом. Нопобеда будет достигнута тяжелой ценой. ​
 +
 +Работать перестанет все, включая только что переписанный русским программистом код. Это повергнет русского программиста в задумчивость на целых два дня, после чего он сделает,​ в общем-то,​ предсказуемый вывод о том, что дизайн с самого начала был неправильным,​ и все надо переписать. На это нам нужна неделя. Да, неделя,​ не больше. Канадский программист смело бросится налаживать все, и станет еще хуже, хотя казалось бы... Эта суета выведет из медитации индуса,​ который придумает и вовсе гениальное решение - отбранчить код. Согласно его плану, мы теперь будем поддерживать две версии одного и того же кода - одну работающую но с Багом, другую без Бага, но не работающую. Русский программист услышав об этом плане, сломает линейку об стол и дома обзовет жену дурой, но на митинге возразить не решится.
 +
 +К счастью,​ все это не сильно влияет на дела фирмы, поскольку продукт продается и так. Поэтому менеджмент ходит в целом довольный и не устает напоминать всем, что они отобраны как лучшие среди лучших. И что мы давно доказали свою способность выпускать продукт тем, что выпускаем его иногда.
 +
 +===== >>>>​ выноски =====
 +
 +  - столкнувшись с необъяснимой ошибок,​ начинающие программисты обычно сваливают вину на компилятор,​ хотя в подавляющем большинстве случаев они виноваты сами. невнимательное чтение документации и небрежный стиль кодирования — вот основные враги программиста;​
 +  - тестируйте программу только на заведомо исправном оборудовании и ни в коем случае не разгоняйте чтобы там ни было — поиски черной кошки, которой нет, в черной комнате,​ которой никогда не было, отнимают много времени и усилий. ходит легенда,​ что один такой разработчик,​ битый месяц гоняющий бага в программе,​ под конец нашел его… в блоке питания. по одной версии блок питания был зверски разрублен топором,​ по другой — повешен за провод на стену (в назидание окружающим);​
 +  - как разобраться в файле дампа? нужно знать язык ассемблера! Без этого вам никогда не стать настоящим профессионалом!
 +  - http://​www.testingcraft.com/​ проект сдох, но все еще есть куча полезных ссылок по тестированию;​
 +