Меню

Что такое плавающие ошибки

Петя вспоминает, что как-то уже встречал эту ошибку, но она ни разу не повторилась. Он, разумеется, сразу же перепроверяет все кейсы, связанные с сообщениями пользователей, но у него всё точно по ТЗ и никаких ошибок.

Так Петя встретил свой первый плавающий баг.

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

ПЛАВАЮЩИЙ БАГ – это ошибка, которая в одних и тех же условиях то воспроизводится, то нет.

Однако важно понимать, что любая ошибка, даже плавающая, имеет причины появления. И поиск причины – самое сложное в охоте на плавающий баг.

Основные ошибки при охоте

Какие ошибки может допустить Петя при охоте на свой первый плавающий баг?

1. Ринуться с головой в выяснение причин.
Так делать не стоит. Важно сразу понять, насколько ошибка критична, и, уже отталкиваясь от этого, действовать или…

Или оставить всё как есть. Потому что поиск плавающих багов – это всегда большие затраты времени не только QA отдела, но и отдела разработки.

Также не стоит паниковать: баги не вызваны злыми духами и в каждом проекте их в избытке. Просто большинство себя либо не проявило, либо не было замечено.

Также важно удостовериться, что баг действительно плавающий. Будет весьма глупо потратить много часов своей (и вдобавок чужой) работы на отлов ошибки, фикс которой вы пропустили.

Также стоит убедиться, что ошибка является так называемым тестировщиками “лотерейным” багом, то есть багом, всплывающим из-за выпадения какого-то рандома в проверяемом ПО.

2. Накосячить с оформлением тикета в баг-трекере.
Для исправления ошибки разработчику важно иметь максимум полезной информации и минимум бесполезной. Если всё же ошибка критическая и мы беремся за её поиск, то необходимо взять кейс, при котором хотя бы единожды воспроизвелся баг, и пройти его несколько раз. Также по возможности исключить некоторые пункты. Особенно критически нужно относиться к словам очевидцев (пожаловавшихся пользователей). Потому что пользователь без труда что-нибудь напутает, а вас в ваших поисках это уведет совершенно не в ту степь.

Как охотиться?

А дальше есть множество способов работы, которые можно применять и каждый из них по-своему хорош:

1) Подключите к процессу стороннего человека, а лучше нескольких. Свежий взгляд часто замечает то, что вашему замыленному взгляду кажется несущественным.

2) Ищите инструменты, которые помогут вам точнее отслеживать проблему. Например, логи проекта (технические сообщения от программы, падающие в консоль или её аналог) позволят вам не ломать голову над тем, как же воспроизвести только что сломанное ПО. А вместо этого сразу отдать разработчику то, что укажет, что именно сломалось под капотом программы.

3) Налаживайте коммуникацию с пользователями. Спрашивайте детали у тех, кому не повезло столкнуться с плавающей ошибкой. Возможно, именно их помощь будет ключевой. Но не будьте излишне уверены в чужих словах.

4) Поищите в списке багов похожие проблемы. Иногда информация о похожих багах помогает в исправлении.

5) Упрощайте и усложняйте всё. Меняйте переменные. Упрощение поможет точнее локализовать баг, а усложнение приведет к проверкам, казалось бы, несвязанных вещей или нахождению лотерейных багов, о которых мы говорили выше.

6) Провести стресс-тесты. Возможно, ошибка случилась из-за чрезмерной нагрузки.

7) Поговорите с разработчиками о том, чтобы поставить “ловушку” (функцию в программе, сообщающую о том, что что-то не так), которая даст необходимую информацию.

8) Если ничего не помогает, то дайте проблеме отлежаться. Может, она решится сама собой, а может, вы найдете необходимую информацию.

_______
Пост написан совместно с одним из наших преподавателей (Андрей Высоцкий).
В следующем посте рассмотрим, как на практике происходит поиск плавающих багов и поговорим про ошибке, которые допускаются при их поиске.

Сложный код, плавающие баги и инсайт

// 7 февраля, 2011 // Doctrine, PHP, Веб-разработка

Мы три дня ловили баг. Есть такая противная категория ошибок — плавающие баги.  Гейзенбаг (англ. Heisenbug) — термин, используемый в программировании для описания программной ошибки, которая исчезает или меняет свои свойства при попытке её обнаружения. Это слово, в отличие от слова «баг», в русском языке практически не используется. Не полностью идентичный, но достаточно близкий по значению русскоязычный термин — «плавающая ошибка». Примером могут являться ошибки, которые проявляются в окончательном варианте программы (релизе), однако не видны в режиме отладки, или ошибки синхронизации в многопоточном приложении.

Обычно при отладке такого бага, очень трудно его локализовать, т.е. повторить, добиться его стабильного возникновения. Он то появляется, то исчезает. Программист проверяет код, и думая, что он уже отлажен делает коммит на продакшен сервер (ну или на тест-сервер). А потом тестировщик (привет Денис:) обнаруживает его, и возвращает программисту. Тикет по этому багу гуляет из «test» в «new» и обратно по многу раз. Это утомляет всех.
Как правило, при анализе ошибок такого типа, приходится полностью перебирать движок, предлагаются самые неимоверные варианты, по дороге находится и исправляется куча других багов, но этот восстает аки Феникс. Тогда для меня становится делом чести прищучить его. Люблю сложные задачки 🙂

Эта ошибка состояла в том, что периодически на одной странице веб-приложения отключался автозагрузчик классов. До этого он работал, везде он работал, а на одной странице брал и отрубался. Через раз. Иногда мог по 6 раз работать, а потом выключиться. Мы начали грешить на кэширование — отключили. Опкод кэшер — тоже выключили. Потом при ручном отключении конкретных подсистем кэша, всё было нормально, начали включать по одной, всё работало. Включили все — работает. А не должно бы. Тикет пошел в «test», но потом вернулся обратно. «Опять !@#$!@$ глючит» — сказал нам тестировщик. Мы долго думали.

Были сделаны самые невероятные предположения — операционная система, броузер, броузер и операционная система, время суток, погода, дайте нам хоть что-то, за что можно зацепиться! Но нет. Вечер пятницы, а ошибка, так и не исправлена. Посудив, что утро вечера мудренее, мы отправились отдыхать. Благо в субботу у нас намечался корпоратив, во время которого мы славно отдохнули, проветрив мозги.

Иногда бывает так, что ты усиленно думаешь над проблемой, а она не решается. Магии в PHP нет. Есть логика. Как говорил один мой преподаватель — «машина она железная, чего ей скажешь — то она и делает». Так что надо искать условия, проверять, думать.

У меня есть «правило Ч«, которому научила военка. Например, я всегда жду людей строго определённое время (которое конечно зависит от отстоятельств). Это избавляет от терзаний вида, «ну подожду ещё 5 минуточек, может придёт». Время вышло, я ущёл. Тоже и с кодингом, нельзя биться головой в стену, есть определённое время на решение проблемы, если оно истекло — значит что-то не так, значит неправильно действуешь.

Когда бьешься долго над такими типами ошибок, пытаешься представить себя машиной, интерпретатором, пропуская через свой разум код программы, вспоминаешь связи между кусками кода, модулями и классами, моделями и видами, контроллерами и действиями. Ты загружаешь программу в своё сознание. Не задумывались об этом? Ошибка не решается, вы идёте гулять/курить/в магазин/гости/бассейн/проветриться и перестаете думать над этой проблемой. Она вытесняется из фокуса сознания. И тут вступает в игру подсознание. Ты вроде бы не думаешь о проблеме, но иногда на тебя словно спускается озарение, инсайт, идея, которую тут же хочется проверить, попробовать, а вдруг — это решение!

Сейчас был именно этот момент. А ведь в тексте ошибки, которую показывал PHP в путях были пути, которые выставляет утилита, а не сам движок. Тут надо сказать, что кроме большого движка веб-приложения, у нас есть несколько утилит, которые делают какую-нибудь полезную работу, они маленькие быстрые (потому что не загружают Zend Framework) и поэтому мы используем их для ряда служебных задач. НО, они совсем ничего не знают о моделях, контроллерах, видах и т.д. Они не умеют грузить модели, у них свой автозагрузчик классов. Стоп! Свой автозагрузчик. А может быть где-то в коде движка инклудятся утилита, которая грузит автозагрузчик, а он затирает пути set_include_path() к классам, которые можно грузить автоматически. Да, это оказалось так. Эврика, инсайт! За это я и люблю свою работу!

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

Что же после всего хочется сказать?

1. Не делайте ваш код сложным. Поменьше магии, нам в работе итак её хватает. Эй, помееьше магии в прямом смысле! Например, в Doctrine есть магические функции вида $model->findByUserIdOrSessionId($user_id, $session_id).  Красиво? Да, но такой функции нет. Что происходит, когда она вызывается. Происходит вызов служебной функции __call(), затем вызов класаа, поиск по массиву (практически парсинг названия функции), составление DQL-запроса, его выполнение и возврат. Сколько лишних операций, зато красиво. Что надо вам? С головой подходите к работе, делайте ваш код понятным, но не перебарщивайте с магией.
2. Просчитывайте варианты. Пытайтесь думать, как машина, смотрите по каким ветвям может пройти программа.
3. Не делайте код сложным. Упрощайте! Делайте функции с говорящими именами, длинные методы разбивайте на маленькие, используйте принцип DRY в своей работе!
4. Отдыхайте! Нашим мозгам тоже нужен отдых, отвлечься от работы очень часто бывает гораздо полезнее, чем сидеть всю ночь над кодом!

Литература

http://trenings.ru/OnLine_treningi/PsihoTrening/PsihoTrening_5-e_zanyatie._Fokus_vnimaniya.html

Спасибо!

Если вам помогла статья, или вы хотите поддержать мои исследования и блог — вот лучший способ сделать это:

Автор: Джеймс Бах (James Bach)

Оригинал статьи: http://www.satisfice.com/blog/archives/34

Перевод: Ольга Алифанова

Способность уверенно исследовать плавающие баги – одна из характеристик отличного тестировщика. Самые захватывающие истории, которые я слышал от тестировщиков, были про охоту на «белых китов» в океане сложного кода.

В отличие от багов, которые загадочным образом воспроизводятся постоянно, плавающий баг – это больше проблема тестирования, нежели разработки. Многие программисты просто не хотят гоняться за причинами таких проблем, если есть куда более доступные жертвы.

Вообще-то плавающие баги не должны вас удивлять. Все программирование, можно сказать, крутится вокруг контроля неустойчивого поведения. Итак, о чем речь?

Нас не беспокоит неустойчивое поведение, если оно а) так и задумано б) не несет в себе тайны, даже если оно не вполне предсказуемо. Например, результат подброшенной монеты или шанс выбить 777 в игровом автомате – вполне себе неустойчивое поведение. Даже загадочное неустойчивое поведение не волнует нас, если мы уверены, что оно не вызовет проблем. Когда я тестирую, я не думаю о магнитных полях или мелких скачках напряжения, хотя они влияют на мою работу постоянно.

Многие плавающие баги никем не обнаружены просто потому, что они еще ни разу не проявились (или проявились, но никто их не заметил). Все, что мы можем сделать – это позаботиться о создании наилучшего тестового покрытия и придерживаться этого подхода. Нет в мире алгоритмов для автоматического определения или предотвращения всех плавающих багов.

Итак, то, что обычно называется плавающим багом – это загадочное, нежелательное поведение системы, которое наблюдалось как минимум единожды, и которое мы не можем спровоцировать повторно.

Наша задача – превратить плавающий баг в обычный, раскрыв тайны, окружающие его. После этого он становится головной болью программистов.

Принципы работы с плавающими багами

  • Успокойтесь, они, скорее всего, не вызваны злыми духами.
  • Если это случилось один раз – скорее всего, это произойдет снова.
  • Если баг не был исправлен, вряд ли он самоликвидировался навсегда.
  • С осторожностью относитесь к исправлениям плавающих багов. Исправленный баг и неисправленный плавающий баг неотличимы друг от друга по определению в течение какого-то времени и/или при определенных условиях.
  • Любое состояние системы, переход в которое занимает длительное время в обычных обстоятельствах, может быть мгновенно достижимым в непредсказуемых ситуациях.
  • Сложное и запутанное поведение обычно спровоцировано чем-то довольно простым.
  • Сложное и запутанное поведение иногда спровоцировано целым комплексом причин.
  • Плавающие баги зачастую могут рассказать вам много интересного про ваш продукт.
  • Очень легко поддаться уверенности, что ваше предположение об источнике проблемы разумно, остроумно и имеет смысл – оно просто неверное.
  • Ключ к разгадке может находиться в руках другого человека.
  • Возможно, что баг, «плавающий» на тест-стенде, легко воспроизведется на проде.
  • Принцип Pentium 1994: плавающая техническая проблема может привести к систематическим и очень дорогостоящим проблемам бизнеса.
  • Проблема может быть плавающей, но риск, связанный с ней, стабилен.
  • Чем выше тестируемость продукта, тем легче исследовать плавающие баги.
  • Когда вы исключили все невозможное, то то, что осталось, каким бы невероятным оно ни было, уже нанесло существенный урон! Поэтому не ждите момента, когда вы полностью раскусите этот орешек, заводите плавающие баги как можно раньше!
  • Если вы не успеваете ущучить плавающий баг до релиза, сделайте все, что в ваших силах, чтобы все-таки выловить его и исправить. Получайте удовольствие от процесса, так сказать.

Общие советы по исследованию плавающих проблем

  • Перепроверьте свои базовые предположения: используете ли вы именно тот компьютер? Тестируете ли вы именно то, что нужно? Верны ли ваши наблюдения?
  • Свидетельства очевидцев могут оставить за бортом важную информацию, поэтому слушайте, но не нужно излишней уверенности в чужих словах.
  • Пригласите других наблюдателей, подключите к процессу большее количество людей.
  • Мотивируйте людей сообщать о плавающих проблемах.
  • Если вам сказали, что причина проблемы ТОЧНО не в этом, обратите на ЭТО особое внимание.
  • Проверьте сайты технической поддержки каждого из сторонних компонентов, который используется в вашем приложении. Возможно, ваша проблема там указана.
  • Ищите инструменты, которые помогут вам наблюдать за поведением системы и контролировать его.
  • Налаживайте коммуникацию с наблюдателями (особенно с реальными пользователями).
  • Соберите все загадочные баги в одном месте, чтобы легче было отслеживать закономерности.
  • Просмотрите список багов, поищите в нем похожие на плавающий баг проблемы.
  • Точнее зафиксируйте свои наблюдения, пользуйтесь инструментами.
  • Улучшите тестируемость вашего продукта, добавьте логирование и интерфейсы с поддержкой сценариев.
  • Точнее контролируйте данные, которые вы вводите (включая их последовательность, время, типы, размеры, источники, итерации, комбинации).
  • Систематически покрывайте тестами данные ввода и состояния системы.
  • Сохраняйте абсолютно все логи. Возможно, позже вам понадобится сравнить новые логи со старыми.
  • Если проблема чаще возникает в одних ситуациях и реже – в других, проведите статистический анализ разницы между закономерностями в этих ситуациях.
  • Попробуйте контролировать то, что, с вашей точки зрения, не имеет значения.
  • Упрощайте все. Попытайтесь менять одну переменную за раз, попробуйте разбить систему на части (помогает понять и изолировать проблему).
  • Усложняйте. Попытайтесь изменить несколько переменных за раз, устройте в системе бардак (помогает выловить «лотерейные» баги).
  • Добавьте элемент случайности в состояния системы и обрабатываемые данные (возможно, при помощи менее жесткого контроля), чтобы добиться состояний, которые, возможно, не вписываются в ваш обычный шаблон использования.
  • Создайте искусственный стресс (высокая нагрузка, большие объемы данных).
  • Поставьте ловушку, чтобы при возникновении проблемы в следующий раз вам удалось изучить ее получше.
  • Подумайте насчет код-ревью.
  • Поищите конфликты между компонентами, созданными разными компаниями.
  • Собирайте и храните рассказы о локализации плавающих багов.
  • Систематически размышляйте о причинах плавающих багов (см. ниже).
  • Опасайтесь потратить кучу времени на мелкий баг, всегда спрашивайте себя, стоит ли он того.
  • Если ничего не помогает, дайте проблеме отлежаться, поработайте над чем-нибудь другим и последите, не появится ли она снова.

Возможные причины плавающих багов

Локализуя плавающий баг, поразмышляйте о типах причин, вызывающих подобные проблемы. Ниже – список эвристик, которым можно пользоваться для подобного анализа. Некоторые из причин дублируются, потому что их можно рассматривать с разных точек зрения.

Вероятная причина номер 1: Система ведет себя точно так же, как и вела. Кажущийся сбой поведения – артефакт, относящийся к вашему восприятию.

  • Плохие наблюдения. Наблюдатель мог быть невнимательным — например, подвергнуться «Слепоте невнимания» – феномену, когда мозг занят чем-то еще, и человек не видит того, что находится у него прямо перед носом. Если показать ему то же самое еще раз, он увидит нечто новое для себя и предположит, что раньше такого не происходило. К тому же так работает ряд оптических иллюзий, демонстрируя кажущееся иным поведение при том, что ровным счетом ничего не изменилось.
  • Наблюдения, не относящиеся к делу. Наблюдатель обращает внимание на различия, которые не имеют значения. То, что важно, остается стабильным. Это случается, когда наблюдатель чересчур пристально бдит за мелочами.
  • Плохая память. Возможно, наблюдатель плохо запомнил свои впечатления, или записи, фиксирующие проблему, повреждены. Когда вы наблюдаете за чем-то, ваш мозг обрабатывает кучу данных! Он немедленно упаковывает их и пытается связать их с другими данными, при этом важная информация может потеряться. К тому же в процессе разработки и тестирования мы повторяем одно и то же довольно часто, и можем запутаться в деталях каждого отдельного наблюдения.
  • Ложная атрибуция. Наблюдатель приписывает свое наблюдение не тому, чему нужно. К примеру, «Microsoft Word упал» может означать, что упала Windows, и причина падения не имеет к Word ни малейшего отношения. Word вообще ничего не делал. Этот феномен еще называют «ложной корреляцией» и часто встречается, когда одно событие следует сразу же после другого, и кажется, что это причина и следствие. Благодаря ложной корреляции плавающие баги зачастую считаются обычными багами, появившимися из-за крайне сложного и маловероятного комплекса причин.
  • Искажение фактов. Наблюдатель, возможно, неверно передает информацию. Причин может быть множество – например, вполне невинная: он настолько уверен в своих предположениях, что передает свои наблюдения определенным образом. Как-то раз я спросил сына, подключена ли его нерабочая Playstation к сети. «Конечно», огрызнулся он. Попробовав решить проблему так и сяк, я решил, что блок питания умер. После чего посмотрел на него и обнаружил, что штекер не включен в розетку.
  • Ненадежный оракул. Мнение наблюдателя насчет того, что является проблемой, а что нет, постоянно меняется. У нас может создаться впечатление, что проблема плавающая, только потому, что некоторые люди – иногда – не считают это поведение проблемой, даже если оно вполне предсказуемое. Другие люди могут отнестись к вопросу иначе, и даже один и тот же человек может поменять свое мнение.
  • Ненадежная коммуникация. Коммуникация с наблюдателем может быть неполной. У нас создается ощущение, что мы нашли плавающий баг, потому что сообщения о нем не всегда до нас доходят, даже если проблема вполне предсказуема. Фраза «Мне кажется, проблема больше не проявляется» может означать, что люди просто перестали о ней сообщать.

Вероятная причина номер 2: Система ведет себя иначе, потому что это другая система.

  • Deus ex machina. Разработчик мог специально что-то поменять, а потом вернул все назад. Это часто случается, когда над разными частями платформы одновременно работает несколько разработчиков или команд, не координируя свои действия друг с другом. Другой вариант – злонамеренные модификации приложения хакерами.
  • Случайное изменение. Разработчик мог внести правки случайно. Возможно, незапланированные побочные эффекты и приводят к возникновению плавающих проблем. К тому же разработчик может ошибиться и выкатить правки на прод вместо тест-стенда.
  • Другая платформа. Один из компонентов платформы мог быть заменен или переконфигурирован. Администратор или пользователь могли специально или случайно изменить что-то в компоненте, от которого зависит продукт. Частый источник таких проблем – автоматические обновления Windows, изменения в конфигурации памяти и дискового пространства.
  • Проблемы железа. Может, какой-то физический компонент системы сбоит время от времени. Перебои могут быть вызваны естественными вариациями в его работе, магнитными полями, излишне высокой температурой, низким зарядом батареи, плохим обслуживанием или физическим шоком.
  • Чужая система. В работу вашего приложения может вмешаться какое-нибудь еще. Например, в веб-тестировании я могу иногда получать неверные результаты из-за прокси-сервера провайдера, который загружает кэшированную версию страницы в неподходящий момент. Другие примеры такого вмешательства – это сканирование на вирусы, обновления системы, другие программы или та же самая программа, запущенная повторно.
  • Проблемы кода. Может, проблема кроется в том, как написан код. Один из худших багов, созданных моими руками (худших в смысле тяжести поиска причины) возникал из-за кода в игре, который иногда перезаписывал данные в совершенно другой части программы. Из-за природы этих данных игра не падала — зато поврежденная функция передавала управление функции, которая следовала сразу за ней в памяти приложения. Мне понадобилось несколько дней (и эмулятор чипа), чтобы это понять.
  • Раздвоение личности. То, что вы понимаете под системой, может на самом деле состоять из нескольких систем, мимикрирующих под единую. К примеру, я могу получать разные результаты от Google в зависимости от того, на какой сервер Гугла я попадаю. Или разные компьютеры тест-команды имеют разные версии ключевого компонента, или я опечатался в URL и случайно начал тестировать на другом сервере.
  • Человеческий фактор. Возможно, за запуск части системы отвечает человек, и этот человек ведет себя по-разному.

Вероятная причина номер 3. Система ведет себя иначе, потому что находится в другом состоянии.

  • Замершее условие. Решение, которое должно основываться на статусе условия, могло перестать проверять это условие, застряв в состоянии «всегда да» или «всегда нет».
  • Неверная инициализация. Одна или несколько переменных не инициализируются. В результате стартовое состояние расчетов становится зависимым от предыдущих расчетов той же или иной функции.
  • Отказ в ресурсах. Критически важный файл, поток или другая переменная недоступны для системы. Это может произойти, потому что они не существуют, повреждены или заблокированы другим процессом.
  • Прогрессивное повреждение данных. Система могла превратиться в бардак постепенно из-за накапливающихся мелких ошибок. Например, это может проявляться как рассинхрон временных циклов, или ошибка округления при сложных или рефлексивных расчетах.
  • Прогрессивная дестабилизация. Классический пример многоэтапного отказа. Первая часть бага создает нестабильность – например, указатель массива ведет в никуда при определенных условиях, однако внешне баг никак не проявляет себя. Из-за этого невидимого сбоя система переходит ко второму этапу – к видимому сбою, который происходит позднее в комбинации с какими-то другими условиями. Увязать эти события между собой очень трудно из-за задержки между первопричиной и последствиями.
  • Переполнение. Какой-либо контейнер может переполниться, провоцируя сбой или обработку исключений. В эпоху больших объемов памяти о возможности переполнения часто забывают. Даже если исключение корректно обрабатывается, сам процесс обработки, взаимодействуя с другими функциями системы, может вызвать плавающую проблему.
  • Редко встречающиеся функции. Некоторые функции системы используются настолько редко, что мы про них забываем. Это обработка исключений, внутренняя сборка мусора, автосохранение, функции обслуживания. Когда они срабатывают, они могут неожиданно вступить в конфликт с другими функциями или условиями. Обращайте особое внимание на скрытые и автоматические функции.
  • Другой режим или настройки. Система может работать при разнообразных режимах, и пользователь установил какой-то другой. Отличия могут быть незаметны с первого взгляда.

Вероятная причина номер 4. Система ведет себя иначе, потому что получила другие вводные данные.

  • Случайный ввод. Пользвоатель мог ввести что-то или изменить ввод таким образом, что это не должно было ни на что повлиять – но все же повлияло. Это можно назвать синдромом Умного Ганса – лошади, которая периодически умудрялась решать математические задачи. В результате Оскар Пфунгст обнаружил, что лошадь реагировала на непроизвольные микродвижения своего хозяина. В своей области я как-то раз столкнулся с плавающим багом, который возникал, когда солнце проникало через мое окно и падало на оптический сенсор моей мыши. Погодные условия никак не должны были повлиять на ввод данных в приложение – однако влияли. Более распространенный пример странного поведения – баги, возникающие, когда вы используете клавиатуру, а не мышь, чтобы отдать какую-либо команду. Случайный ввод может быть невидимым глазу без специальных инструментов и рекордеров. Например, два идентичных текста в RFT-формате, один из которых сохранен через Word, а другой через Wordpad, выглядят очень похоже, но совсем не идентичны.
  • Секретные границы и условия. Программа может вести себя иначе из-за скрытых границ или областей падения, которые никем не задокументированы и которых вы не ожидаете, выстраивая свою ментальную модель продукта. Как-то раз я тестировал поиск, который вел себя совершенно иначе, если количество найденных записей равнялось тысяче или пятидесяти тысячам. Я обнаружил эти скрытые границы совершенно случайно.
  • Другой профиль использования. Использование приложений некоторыми пользователями может чем-то специфически отличаться. Их предположения насчет ввода ведут к другим результатам вывода. Пользователи с определенным опытом – например, программисты – могут систематически замечать (или не замечать) определенные типы поведения приложения.
  • Призрачный ввод. Источником ввода может быть не пользователь, а машина. Такой ввод часто незаметен пользователю, и включает вариации из-за разных файлов, различные сигналы от периферийных устройств, или данные, поступающие по сети.
  • Deus Ex Machina. Кто-то еще взаимодействует с продуктом в то же самое время, что и пользователь – другой тестировщик, другой пользователь, хакер.
  • Дефектный ввод. Данные могли быть повреждены или перехвачены на пути к системе. Особенно это касается клиент-серверных приложений.
  • Время в качестве вводных данных. Плавающие баги могут зависеть и от времени как такового. Время всегда меняется вне зависимости от того, удалось ли вам проконтролировать и зафиксировать все остальное. Когда время и дата, или временные интервалы, используются в качестве данных, баги могут проявляться только в определенное время.
  • Временная лотерея. Вариации ввода, которые обычно не имеют значения, могут внезапно стать критическими в определенное время или при определенной нагрузке. От этой проблемы страдал Mars Rover – окно уязвимости в три миллисекунды, когда операция записи могла писать в защищенную часть памяти.
  • Комбинационная лотерея. Вариации ввода, которые обычно не имеют значения, могут вызвать проблему, если они скомбинированы определенным образом.

Вероятная причина номер 5. Прочие причины связаны с тем, что ваша ментальная модель системы и того, что на нее влияет, неверна или неполна.

  • Вы можете не знать о всех переменных, влияющих на систему.
  • Вы можете не знать об источниках искажений, влияющих на ваши наблюдения.
  • Вы можете не знать об инструментах, которые помогли бы вам понять систему или наблюдать за ней.
  • Вы можете не знать всех границ системы и характеристик этих границ.
  • В системе может вообще не быть функции, которую вы в ней предполагаете, или в нее включены дополнительные функции.
  • Сложный алгоритм может периодически вести себя неожиданно – например, математический хаос, который выглядит, как отклонение от нормы.

Обсудить в форуме

Неуловимые, раздражающие, заставляющие сомневаться – это все про плавающие баги. Попробуем разобраться, что это такое и с чем его едят.
Ловить ошибки – основная задача тестировщика, но бывают ситуации, когда ошибка зафиксирована, а повторить ее не получается. Загадочное поведение программы, которое невозможно воспроизвести или зафиксировать – одно из самых сложных явлений в работе инженеров по QA.
Обнаружить, поймать и превратить в обычный – схема охоты на плавающий баг. Говорят, будто плавающие баги есть в любой программе, просто некоторые из них еще ни разу не проявились.

Что такое плавающий баг?​

Нежелательное поведение программы, которое возникает спонтанно и непредсказуемо, называют плавающим багом или гейзенбагом с отсылкой к немецкому физику Вернеру Карлу Гейзенбергу – автору принципа неопределенности из квантовой физики.

Вернер Гейзенберг говорил, что «чем более пристально вы глядите на один предмет, тем меньше внимания вы уделяете чему-то еще».

По правилам немецкого и английского языков, фамилию физика и название бага необходимо читать как «хейзен», но в русском языке адаптировалось произношение «гейзен».

Самые интересные случаи из практики тестировщиков чаще всего связаны именно с этим типом багов, т.е. с загадочными проблемами, которыми не станут заниматься разработчики, пока не будут понятны шаги воспроизведения. Определить, где они «живут» – задача специалистов по QA. В тоже время не всякое неустойчивое поведение равно плавающему багу. Например, неопределенность результатов при броске игральных костей не несет в себе угрозы и не является гейзенбагом.
Алгоритмов автоматического определения плавающих багов пока не придумали, поэтому инженерам по QA приходится создавать лучшие тестовые покрытия из возможных и учиться локализовать проблемы.

31596e84cf63d2db3cfaa1412bfeca62.jpg

Как работать с плавающими багами?​

Для избавления от плавающих багов используется традиционный принцип «повторить и разобрать», но с некоторыми оговорками:

  1. Как проверить, что перед вами плавающий баг, а не обычный? Во втором случае вы понимаете, откуда взялась проблема, а в первом – нет. Именно поэтому внимательность и способность ничего не упускать – одно из главных достоинств тестировщика. Самое смешное, неисправленный плавающий и исправленный обычный баги очень похожи – они не воспроизводятся, но плавающий баг может в любой момент вернуться.
  2. Чем выше тестируемость продукта, тем легче найти в нем плавающий баг.
  3. Любая произошедшая по непонятным причинам ошибка может вернуться. «Прошло само» не работает. Код – это четкая структура, поэтому умение локализовать баги важно развивать. Этот навык пригодится, когда ошибка будет воспроизведена, но как – непонятно.
  4. Плавающие баги могут легко воспроизводиться в продуктивной среде и плохо проявляться или вовсе не проявляться в тестовой. Это важно учитывать и соотносить с процессом релиза системы.
  5. В появлении плавающих ошибок может быть виноват целый комплекс причин, поэтому при тестировании используйте таблицы со множеством вариантов. Иногда простой перебор сочетаний различных условий приводит к успеху.
  6. Никогда не пытайтесь скрыть плавающий баг от заказчиков и пользователей. В будущем это может привести к еще большим проблемам.

Яркий пример того, как попытка Intel скрыть ошибку привела к убыткам:
В выпускавшихся Intel в 1994 году процессорах Pentium была проблема с некорректным делением чисел с плавающей запятой. В компании посчитали, что дефект может повлиять на работу очень узкого круга пользователей, и решили его скрыть. Возникший в обществе резонанс заставил Intel изменить позицию и принес убытков на $475 млн.

Как локализовать ошибку, если первичный анализ ничего не дал?​

  • Обратите внимание на межфункциональные и межсистемные блоки. Ошибки часто живут на границах.
  • Изучайте логи и трассировки.
  • Создавайте аналогичных тестовых пользователей с одинаковыми правами и полномочиями, проверяйте работу программы на них.
  • Попробуйте тест на других машинах с аналогичными и отличающимися характеристиками.
  • Исследуйте от частного к общему всю среду, в которой работает программа, и наоборот, попробуйте пойти от ошибки. Какие условия могут быть важны для ее проявления?

5521f17c0dc7950036a8e945f7805594.jpg

Почему возникают плавающие баги?​

После успешной локализации плавающего бага или другой сложной ошибки, важно разобрать и зафиксировать причины ее появления. Такие кейсы – настоящие жемчужины профессионального опыта.
Вот примерный список причин, который поможет в случае, если вдохновение вас покинуло:

  1. Переполнение. Какое-либо поле или контейнер могут переполняться и провоцировать сбой при корректной работе связанных с ним функций. В эпоху больших запасов памяти тестировщики часто упускают это из вида. Например, внутри системы могут считаться огромные суммарные значения, и она окажется не способна переварить такое количество символов.
  2. Проблема кода, которую также называют «Проблемой ТЗ». Зачастую плавающий баг связан с тем, как написан код (мы не рассматриваем сейчас некачественную работу, только непредсказуемую). Например, составителю технического задания на разработку очевидно, что при выборке данных нужно взять некий уникальный объект, но разработчик этого знать не может и прописывает свободный выбор. Бывает, что код написан верно, но какое-то сочетание условий приводит не к поломке, а к подмене функций. В этом месте не будет ошибки, но будет неверная работа программы в целом.
  3. Проблемы ввода. Например, в поле свободного ввода пользователь внес непредусмотренные алгоритмом данные, либо ошибочный символ (запятую вместо точки и т.д.). Сюда же относятся ошибки, связанные с использованием клавиатуры вместо мыши или ошибки призрачного ввода, когда данные вносит машина (особенно где-то в середине расчетов). В таких случаях проблему можно обнаружить, проставляя контрольные «стопы» и проверяя работу кода по частям. Еще один вариант – дефектный ввод, когда данные клиент-серверных приложений повреждены или переданы с ошибкой.
  4. Проблемы оборудования возникают, когда ошибки связаны с физическими факторами. Например, из-за перебоев с электропитанием, высоких температур в серверной, физических повреждений компьютера и т.д.
  5. Случайности. Некая комбинация условий может сработать корректно, но впоследствии привести к ошибке.
  6. Deus Ex Machina. Эти проблемы возникают, когда одновременно с вами кто-то еще использует приложение: разработчик, хакер или другой тестировщик.
  7. Неочевидные моменты. При анализе специалисты часто отбрасывают ненужное, чтобы сосредоточиться на важном, а оказывается, что проблема возникает из-за отброшенных ранее вариантов.
  8. Неизвестное. Невозможно учесть все. Например, в системе может быть функция, о которой тестировщику неизвестно, или некие источники искажений. Внутри приложения могут создаваться незадокументированные переменные и т.д. и т.п.

Еще несколько советов по работе с плавающими багами:​

  • Всегда проверяйте базу. Характеристики компьютера, что тестируете, исходные данные в документах и по факту.
  • Всегда проверяйте информацию со стороны. Не задавайте вопросы в чате пользователю или коллеге, лучше сходите и посмотрите сами.
  • Если кажется, что все уже проверено от и до, причем несколько раз, попросите коллег посмотреть. Без вас и ваших рассказов, с минимумом вводных.
  • Поищите похожие кейсы и посмотрите, как решались такие вопросы раньше.
  • Сохраняйте все логи. Возможно вам придется сравнивать не только исходный и последний, а еще и промежуточные.
  • Проверяйте то, что на первый взгляд не имеет значения. Даже если вам кажется, что это вообще не относится к вопросу. Часто именно оно оказывается искомым.
  • Проверяйте конфликты на границах разных модулей и систем, особенно если интеграция происходит между продуктами разных разработчиков.
  • Если используете сторонние компоненты, обратитесь в техподдержку разработчика.
  • Если нет никакой информации, ничего не помогает, а ошибка по прежнему не найдена, дайте проблеме отстояться. Вернитесь к ней завтра, а сегодня переключите внимание на другие задачи.
  • Не отчаивайтесь. Ищите инструменты, которые помогут решить вопрос – однажды вы обязательно их найдете.
  • Соотнесите пользу от устранения бага и потраченные на это усилия. Иногда достаточно знать о наличии проблемы: это не баг, это фича.

***
Невыявленные ошибки могут приводить к опасным последствиям. В 1962 году космический аппарат Mariner должен был направиться к Венере, но сломалась антенна и он перешел на автопилот, полетев не туда. В итоге аппарат пришлось взорвать над Атлантическим океаном, а произошло это из-за пропущенного при программировании системы навигации в одной из формул символа. Большинство багов не настолько фатальны, да и системы поиска ошибок стали надежнее, но стремительный рост информационных технологий приводит и к росту сложности систем. Найти в них даже обычные ошибки становится все труднее, а количество плавающих багов увеличивается. Их устранение – уже не начальный уровень в работе тестировщика, но тем интереснее решать подобные задачи. Удачи вам в обучении!

Источник статьи: https://proglib.io/p/chto-takoe-geyzenbag-i-kak-s-nim-borotsya-2021-02-28

Регистрация на курс

Плавающий баг?

Ох уж эти мистические «плавающие ошибки». Сколько вокруг них мифов! Но, когда причина найдена, всегда оказывается, что нет плавающих багов, а есть недолокализованные.

Поэтому мы будем учиться локализовывать баги, которые «не воспроизводятся». Учиться искать причину проблемы без помощи разработчика. Учиться смотреть в код и искать причину снаружи. Делить бисекционно и читать логи. В общем, всё, что нужно для воспроизведения!

Воспроизведение ошибки

Если вы не можете вопроизвести ошибку, то:

  1. Прочитайте логи — если есть доступ к логам, всегда в первую очередь смотрим туда! Если у нас веб-приложение, проверьте, что ушло с клиента на сервер.
  2. Проверьте граничные значения — баги часто тусят на границах.
  3. Попробуйте пойти «от обратного» — подумать, как получить саму проблему. Этот метод хорошо работает для багов с кэшом
  4. Составьте таблицу отличий — у вас то все работает. Что отличает ваш кейс от падающего?

Что записывать в таблицу отличий:

  1. Состояние объекта.
  2. Действия, которые с ним выполняли.
  3. Устройство, на котором воспроизводится.
  4. Время выполнения действия (какой релиз?).
  5. Способ воспроизведения (GUI, API).
  6. Количество открытых вкладок в браузере.

В отлове багов помогает понимание того, где копятся баги и какого рода: как влияет на работу системы concurrency, миграция данных, перетипизация переменной внутри кода… Зная последствия, вы будете понимать, что именно записать в таблицу отличий.

Локализация ошибки

Если вы можете воспроизвести баг, но не можете локализовать (слишком много шагов воспроизведения / слишком большой файл, внутри которого есть ошибка), то:

  1. Посмотрите код — возможно, это будет самым быстрым решением.
  2. Используйте бисекционное деление.
  3. Придумайте теорию (на баг влияет то и то) — подтвердите ее, а потом попробуйте опровергнуть.
  4. Выкиньте лишнее из шагов — возможно, именно это сбивает вас с толку

На курсе мы будем рассматривать каждую из техник. Вы сможете сразу применить ее на практике на специально подобранных домашних заданиях, которые показывают именно данную технику или конкретную проблему (миграции данных, стыка интеграции и т.д.)

О курсе

Это практический курс: теории минимум. Типичная лекция — на 10-15 минут. Короткая лекция «как это применять» и тут же практика! Конечно, есть и сложные лекции (на 30 минут). Есть и короткие, на 7 минут. Нет жесткой привязки ко времени лекции. Сколько надо дать теории, столько и говорю, а дальше уже практикуйтесь.

Для локализации багов мы будем применять техники:

  • Черного ящика — как понять, что происходит, если нет доступа в код.
  • Белого ящика — куда можно посмотреть в коде? Смотреть будем на примере приложения folks с открытым исходным java-кодом. Разумеется, у вас на проекте код будет выглядеть по-другому, но зато вы получите представление, где там могут вылезти баги. Баги, которые мы будем рассматривать на курсе, не были внесены в приложение нарочно — так что все приближено к реальности.

Лекции идут в моем фирменном стиле, с картинками. Посмотрите примеры видео на моем youtube-канале, чтобы понять, подходит ли вам такой стиль изложения.

Если вы переживаете, что не сможете что-то сделать (например, задания на просмотр кода), вот вам задача для самопроверки — соберите проект folks и запустите тесты. Инструкция есть, есть даже видео-инструкция по установке java, maven на Windows, Если справитесь, то и все остальное будет вам по плечу!

Программа курса

0. Введение

Что такое локализация и зачем она вообще нужна тестировщику.

Как умение локализовывать задачи экономит время команды и убирает панику.

Как знание о типовых проблемах помогает воспроизводить баги, которые «не воспроизводятся».

1. Уточнение устройств, на которых есть баг

На что обращать внимание при багах «поехала верстка», «текст вылезает за пределы экрана».

Как понять, в какой момент едет верстка? Какие инструменты использовать:

— как замерить размер окна;
— какие есть линейки;
— как посмотреть размер окна в консоли.

2. Исследование состояний

Изучим разные состояния объекта. Если проблема при сохранении — а кого мы вообще сохраняем?

— свежесозданную карточку
— отредактированную
— удаленную

Если у нас есть пользователь, то какой он? А как регистрировался? Как это может повлиять на воспроизведение бага?

3. Использование классов эквивалентности и граничных значений

Что такое классы эквивалентности.

Почему на границах встречаются баги.

Зачем тестировать ноль и как его найти, если у нас не строка ввода?

4. Опровержение своих теорий

Как опровергать свои теории и зачем это нужно.

— таблица отличий;

— метод «найди 5 отличий»;

— принцип минимализма (отсеивание лишнего).

5. Метод бисекционного деления

Что такое бисекционное деление.

Как его использовать тестировщику для локализации бага.

Как оно используется в разработке и к каким проблемам может привести.

6. Чтение логов

Что такое логи. Что в них записывается?

Как читать логи. Как читать стектрейс ошибки.

Логи на сервере и на клиенте.

7. Просмотр запросов с клиента на сервер

На клиенте помимо JS-логов можно смотреть, что вообще ушло на сервер.

Это помогает понять, где произошла ошибка и кто виноват: клиент или сервер. Учимся это делать.

8. Воспроизведение «от обратного»

Техника расследования закрытой комнаты. Наиболее актуальна для багов с кэшом.

Вот есть баг, из кэша достаются неправильные данные. Как воспроизвести? Идем от обратного, пытаемся получить “плохие данные” разными путями.

9. Проверка по коду

Как читать код, при условии, что разработчики идут вам на встречу и разделяют потроха логики и описание объектов.

Читаем java-объект и схему создания БД. Выверяем их на соответствие друг другу.

Внимание: у вас на проекте может быть не java, но принцип все тот же. На примере домашнего задания вы увидите, как можно найти баг, исследуя код

10. Проверка соответствия типов данных

Если поле встречается в коде несколько раз:

— в объектной модели;

— в схеме создания БД;

— где-то еще

Важно проверять соответствие типов. Смотрим, какие встречаются баги на несоответствие типов.

11. Проверка стыка интеграции

Какая бывает интеграция между системами.

Какие бывают баги на стыке интеграции.

Куда смотреть в первую очередь.

12. Проверка данных после миграции

Зачем тестировщику нужна как чистая база, так и база, живущая веками?

Примеры ошибок, вылезающих после миграции и почему они не ловятся на свежей тестовой БД.

13. Проверка concurrency

Типичная ошибка — выполнение CRUD над одним пользователем. Тестировщик может найти баги, переключаясь между вкладками интерфейса. Но это про поиск багов, а не про локализацию, казалось бы, да?

Но о concurrency важно знать, так как параллельная работа может быть и на другом уровне:

  • Идет забор данных из базы по 1000 записей за раз. Что, если в этой 1000 встретились 2 одинаковых клиента? Строка базы заблокирована первым изменением — второе разваливается.
  • Интеграция по SOAP REST. Если запросов много, там тоже можно влететь на изменение одного и того же клиента.

И в этом случае к вам придут “у меня тут ошибка”, а вы понять не можете, откуда она взялась, так как текст невразумительный. Надо помнить про параллельную работу, чтобы локализовать причину.

14. Заключение

Подводим итоги.

Вопросы и ответы

1. Можно ли проходить курс на Linux или Mac?

Да, он не зависит от вашей OS

2. Какие знания нужны для курса?

Общие знания о тестировании — что это вообще такое, как составлять тесты, что такое классы эквивалентности.

Если будете делать ДЗ с просмотром кода (необязательные), то:

  • Базовое понимание языка программирования (любого) — вы не должны падать в обморок от слов string или «тип данных». Но все, что будет нужно для ДЗ, я объясню.
  • Умение устанавливать новые программы — мы будем смотреть в код, а для этого нужно будет установить java, mercurial, maven. Вот инструкция, соберите проект перед записью на курс.

3. Будет ли мне интересно, если я проходил другие ваши курсы?

Если вы прошли «Школу для начинающих тестировщиков» в группе, то встречались с багами в коде folks — но эти задания на курсе необязательные! Так что сильно не влияют на интерес.

Если вы прошли «Логи как инструмент тестировщика», то уже сделали пару обязательных заданий. Но всё равно будет много нового и полезного

4. А скидку то дадут мне за эти ДЗ?

Да, покажите ваш сертификат и получите скидку:

  • Логи как инструмент тестировщика → 20%
  • Школа для начинающих, пройденная в группе → 10%

Как записаться

Напишите на trainings@software-testing.ru: свое ФИО и желаемый курс («курс про техники отлова ошибок»).

.

Способы оплаты

Регистрация и дата ближайшего запуска

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Что такое первичные ошибки это
  • Что такое ошибка свс карты