Уважение к старшим

С легаси кодом не просто.

Это такой специальный код, который был написан в доисторические времена (кажется, в прошлую пятницу). В нём много всего занятного, но мало понятного с первого взгляда. Он что-то делает, но как ему это удаётся — поди разберись. В нём немало странностей, а то и багов, но эти странности благополучно пережили уже несколько версий продукта.

Это такой код, который, с одной стороны, хочется радикально улучшить, а с другой — лучше к нему не прикасаться.

Так вот знаешь, что? Уважай его.

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

Серьёзно, без него у тебя не было бы этой работы.

Он окончательно устарел и пришло-таки время всё исправить? ОК, не вопрос, давай исправлять. Но смотри внимательно: шрамы на старом коде — это следы древних багов. Будешь небрежен — и те же баги повторятся уже в новом коде.

Это легаси. Уважай его.

Новая версия доступна. Обновить?

В жизни приложений, помимо «просто работы», есть масса интересных, пусть и относительно редких событий.

Приложение должно корректно устанавливаться – и нам нужно проверять это.

Приложение должно корректно удаляться (а вдруг!) – и нам нужно проверять это.

Приложение должно корректно обновляться до новой версии – и нам… подождите-подождите… нужно проверять это!

Об апгрейдах и пойдет речь.

Порой мне начинает казаться, что “забыть про апгрейд” – дело не то чтобы обычное, но и нередкое. Ну ладно, забыть – слишком сильно сказано, просто – отложить. Нет, конечно,  процедуру апгрейда сделаем, но пока вот сосредоточимся на доведении функциональности до ума. А потом все разом прикрутим и протестируем.

Ну, мы знаем, сколько того «потом» обычно остается.

Отложить тестирование апгрейда «на потом», проводить такое тестирование на простейших сценариях означает создать проблемы своим клиентам. Хочу отдельно обратить внимание: не потенциальным клиентам, а людям, которые уже используют наш продукт и собирались использовать и дальше. А тут вдруг апгрейд сломался в середине и похоронил все данные за два года работы. Беда… Неудивительно, если клиенты разбегутся, когда что-то пойдет не так.

Ладно, будем считать, что все согласны: аккуратная проверка апгрейдов необходима. Будем проверять.

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

Disclaimer. Все «диалоги» выдуманы, совпадения случайны, слова перепутаны, буквы стоят не на своих местах.

 

Всего лишь набор файлов

Что может пойти не так при апгрейде, если все наше приложение это набор файлов, некоторые из которых – исполняемые?

Да вроде как и особо ничего, а? Берем новые файлы, копируем их на место старых – и готово. Ну для порядка можно «инструкцию апгрейда» написать, что-то вроде

1. Откройте папку, в которую установлено приложение (путь по умолчанию: ХХХ).

2. Скопируйте в нее файлы входящие в новую версию приложения

А лучше – использовать инсталлятор/скрипт, который все файлы сам по местам разложит.

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

— File is locked

– Ой, мы не проверили, что приложение не запущено

 

— Access is denied

– Из под какого пользователя они запускают апгрейд?

 

— Диск полон

– И это в век терабайтных дисков?

 

Или – апгрейд успешно отработал, но приложение не запускается, ведь

— Method not supported

– Упс, а версия одной библиотеки старая. Забыли файл обновить.

 

— ОЙ, какая-то мистика

– Эээ, какая у них древняя ОС. А мы-то писали-проверяли только с последними апдейтами. Хотя прежняя версия и без них работала…

 

— А еще – апгрейд отработал, а версия программы запускается прежняя.

– Хех. У них программа стояла по кастомному пути, а мы «проапгрейдились» в дефолтный :)

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

 

Сервис/демон

С файлами разобрались, пойдем дальше.

Если приложение работает как сервис, то появляются новые интересные проблемы и вопросы в дополнение к рассмотренным в предыдущей части.

— Апгрейд закончился, а сервис работает точно как раньше!

– А рестартануть-то демона мы забыли!

 

— Все работает, все здорово, только сервис перестал запускаться после рестарта сервера.

– Даа, а настройки запуска мы сбросили в «умолчальные» во время апгрейда…

 

— Эй, сервис не работал все время, пока шел апгрейд! Кто мне заплатит за простой бизнеса?

– Хм, надо бы предупреждать, наверное, что сервис будет приостанавливаться во время апгрейда.

 

— Будет простой сервиса? Ну ладно, спасибо, что предупредили. А долгий простой-то?

– Слушай, и правда надо измерять время даунтайма. И, кстати, попробовать как-то минимизировать его.

 

— Я апгрейд выполнил, и уехал по делам. А сервис после апгрейда не запустился оказывается!

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

Да, стоит помнить, что клиенты не для развлечения наши сервисы разворачивают…

 

Клиент-Сервер

Отношения Клиент-Сервер – обычное дело в современных программах. Впрочем, и эти отношения бывают «разной степени усложненности».

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

— Не могу подключиться к серверу. Никогда такой ошибки не было!

– Как мы забыли предупредить, что клиентскую часть тоже надо апгрейдить, а старая версия клиента не сможет работать с сервером???

 

— Мне что, запускать апгрейд на каждом хосте, где стоит какой-то модуль системы??

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

 

— Что-то апгрейд упал на середине…

– ОЙ, вот мы молодцы! Модуль Первый на хосте 01 проапгрейдился и пытается пнуть модуль Второй на хосте 02. А интерфейс общения-то другой после апгрейда! Где ты, обратная совместимость? Даа, на свежих инсталляциях таких проблем не возникает…

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

 

База Данных

Если приложение использует БД, то вкусного еще больше!

Поскольку БД хранит данные (на которых обычно и делается бизнес) то тут в случае фейла используется очень яркая лексика, так что обойдемся без примеров, пусть и выдуманных. Просто перечислю некоторые возможные проблемы.

В целом БД состоит из структуры базы данных (таблицы, индексы, ограничения и т.д.) и собственно данных. Пострадать при апгрейде может и то, и другое.

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

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

С самими данными все хитрее. Их целостность контролировать сложнее, особенно если меняется структура таблиц, и данные переносятся в процессе апгрейда из одного места в другое. Тут, кроме сравнения содержимого БД до и после апгрейда, ничего особо в голову не приходит, а как это сделать правильно – сильно зависит от совершаемых изменений.

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

Другой аспект, на который могут влиять данные, – это время апгрейда. Чем больше в таблице записей, тем больше времени потребуется на изменение структуры этой таблицы и обновление данных в ней. А время потраченное на апгрейд – это время потерянное для бизнеса, и его надо минимизировать. Тут тоже в зависимости от ситуации, требований и ожиданий можно применять разные подходы, от выполнения низкоуровневых функций до использования отдельного сервера для апгрейда БД, пока приложение работает со старым.

Еще интересен случай, когда приложение умеет работать с разными движками БД. SQL, как известно, «немного свой» у каждого движка – MySQL, PgSQL, MSSQL… Всегда есть шанс, что запрос, работающий на PgSQL, окажется нерабочим на MSSQL. На этот случай вариантов немного – нужно проверять на всем, что поддерживается.

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

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

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

 

Инсталер

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

Все ли у него гладко с логикой? Что случится, если в середине апгрейда что-то произойдет, и мы перезапустим апгрейд заново? Пишет ли он логи, какие и куда? Все ли опции работают верно? В конце концов, нет ли орфографических ошибок в интерфейсе?

Нагрузочному тестированию инсталер, наверное, подвергать незачем, но в целом стоит не забывать про этот кусочек продукта

 

Пара общих соображений

Напоследок выскажу еще несколько соображений, не вошедших в предыдущие части.

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

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

Имеет смысл даже после тщательного тестирования быть готовым к возможным проблемам у клиентов, особенно в случае апгрейда баз данных. Наполнение БД на боевых серверах обычное хитрее и разнообразнее того, что можно придумать в тестовой лабе, а значит могут вылезать неучтенные проблемы.

На этом, пожалуй, хватит.

Удачи вам и быстрых беспроблемных апгрейдов.

Лог для бага

Опытным тестировщикам не нужно рассказывать о важности и пользе логов. Логи помогают исследовать проблемы, с их помощью быстрее и точнее локализуются дефекты, они могут рассказать, что происходит на самом деле (а не должно, как нам кажется, происходить).

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

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

Загнал баг без лога – увеличил энтропию

Обнаруженный и зарегистрированный баг – один из основных результатов деятельности тестировщика. Чем точнее описана и локализована проблема, тем проще потом всем жить. Сохранённый и прикрепленный к багу лог помогает повысить точность описания, особенно в случае сложных систем, когда изменение одной настройки приводит к порой неочевидным переменам в поведении программы. Тестировщик может не вспомнить или даже не знать, что для некоторых параметров приложения заданы специфические значения. Соответственно, и в баге он это не упомянет. А лог поможет восстановить, как дело было.

Нашли проблему, что слоны с неба падают; написали, что автобус за углом стоит

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

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

Однако, видимо, не слишком просто, поскольку проблемы с наличием/полезностью логов возникают вновь и вновь.

А что вообще нужно знать, чтобы суметь выкусить адекватную «цитату» из лог-файла?

Для начала надо знать о существовании лога. Ну просто сам факт, что тестируемое приложение куда-то там что-то пишет о своей деятельности.

Второе, что потребуется узнать, – где хранятся логи.

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

Еще немаловажный момент: знать, где и как настраиваются логи. Как включить/выключить логирование, как изменить уровень логирования, как настроить ротацию логов – всё это рано или поздно пригодится и поможет лучше разобраться с той или иной проблемой.

Ну и напоследок нелишне упомянуть, что в случае распределенного приложения зачастую полезно собрать логи на обеих (и более) сторонах. А если ваше приложение взаимодействует с каким-то чужим, то логи этого чужого приложения тоже могут содержать ценную информацию.

Такие вот довольно очевидные и неоригинальные соображения и рекомендации.

Удачи вам, парящих слонов и логов без ошибок.

Границы и края

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

Так, приглядываясь к рискам, которые могут возникнуть в работе, проще всего сперва посмотреть на крайние значения. Кстати нередко вокруг таких крайних значений ломаются копья в различных спорах на тему тестирования (и вообще разработки ПО). Несколько примеров:

Все тесты должны быть описаны так, чтоб их мог выполнить человек с улицы
против
Мы тестируем, а не мараем бумажки

Просто дайте нам приложение, и мы его проверим
против
Чтобы начать тестирование, нам нужны следующие N артефактов: спецификация по стандарту XXX, …

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

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

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

Это не избавляет от проблем, конечно. В каком-то смысле даже, наоборот, добавляет. Ведь до сих пор мы работали с отрезком и двумя крайними точками на нем. В каждом конце отрезка мы довольно хорошо представляем себе, какие тут опасности, и какова степень риска. Теперь же мы провели произвольную (и зачастую не очень четкую) границу где-то внутри отрезка. Областей (классов эквивалентности 🙂 ) стало больше, добавилась лишняя граница. Одновременно изменились и риски. Вероятно, на нашей границе определенный риск стал меньше, чем в крайней точке, однако он все еще ненулевой. Плюс к нему добавился ненулевой риск, тяготеющий к противоположной точке отрезка.

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

Но это еще не все границы, с которыми приходится иметь дело, и, наверное, не самые интересные.

Куда сложнее работать с границами полномочий и ответственности людей и команд в проекте. Тут тоже много споров и мнений. Кто отвечает за качество? Кто должен писать юнит-тесты? Кто имеет право изменять приоритет бага? Как понять, нужно ли вносить данное улучшение или пользователям все равно? Вариантов масса. Тут уже не отрезок, а многомерное пространство; соответственно, и граница получается сложной, и проблем в ее окрестностях больше.

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

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

Удачи вам, четких границ и нестрашных проблем вокруг них.

40 и 90

Как и многие, однажды я задумался о звучании чисел 40 и 90 в русском языке. Почему они — «сорок» и «девяносто», а не «четыредесять» и «девятьдесять»? В том же английском языке все честно: forty и ninety. И у нас вроде бы должно быть так же, если сравнивать с другими числами: «пятьдесят», «семьдесят». Что не так с этими двумя?

Наверняка я не знаю, конечно; разные есть версии на этот счет. Приведу те, что мне самому кажутся интересными и правдоподобными.

40. В христианстве вообще и православии в частности число 40 играет заметную роль — начиная с потопа, длившегося сорок суток, и заканчивая Христом, проповедовавшим сорок месяцев. Православие, как известно, пришло на Русь из Византии, и долгое время службы велись на греческом языке. Оттуда и позаимствовалось звучание числа «четыредесять», и вытеснило постепенно родной вариант.

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

Впрочем, речь не о числах, интересно другое. Замеченная «странность мира» (нелогичные названия чисел) позволила по-новому взглянуть на числа и связь их названий с историей. То есть, заметив странное и покопав в этом направлении, я узнал что-то новое.

Странности мира — отличный источник знаний о мире.

Дефекты продукта — отличный источник знаний о продукте.

Есть куча других, более официальных, что ли, источников информации о продукте. Требования и спецификации, диаграммы и собственно код; много всего интересного, что можно изучать, анализировать и сравнивать друг с другом и действительностью. Однако, это все, в том или ином смысле, — документы. А обнаруженный дефект — он живой и светится. Особенно когда дефект нетривиальный, вызывающий искреннее изумление. Код не все станут читать; требования расскажут, что мы хотим сделать, но не расскажут как. Зато дефект наглядно продемонстрирует неожиданные выверты внутренней логики приложения. Или — не очень наглядно. Но появится повод поинтересоваться, а чего оно вообще так себя ведет?

Черный ящик становится серым (или, если угодно, полупрозрачным) как раз от желания понять, что происходит. Простой и понятный дефект такого желания не вызовет — для этого требуется что-то удивительное, меняющее картину мира и представление о приложении. Ведь пока все идет «нормально», особого желания лезть в кишки продукта и не возникает: и так примерно понятно, что там за логика, и вообще — неинтересно. Но когда приложение выделывает коленце, и оказывается, что «в действительности все не так, как на самом деле», — тогда-то и рождается настоящий исследователь. Он вгрызается в логи, смотрит наконец в код, зудит над ухом у разработчиков, пока ситуация не прояснится.

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

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

Вообще, что неудивительно, тестировщик в некотором смысле узнает продукт через дефекты. Требования и прочие артефакты — это интересно, полезно, здорово, но несколько абстрактно и безжизненно; дефекты — вот где жизнь продукта во всей ее полноте, вот где настоящий вкус этой жизни. Для тестировщика, конечно. Для разработчика, вероятно, все иначе, и продукт это набор классов, шаблонов проектирования, хитрых и гениальных решений, принятых во время разработки — вот что вкусно. А дефекты это так, куда ж без них.

Отчасти поэтому и нужны тестировщики — они смотрят на продукт по-своему.

В жизни, в отличие от разработки ПО, не всегда можно поймать «разработчика» за руку; порой отдельного автора у той или иной странности мира просто нет. Но все равно различные «дефекты» окружающей среды помогают узнать что-то новое об этой самой среде. И, кстати, тенденция та же: чем дальше в лес, тем реже чистое изумление, зато больше нюансов.

Все это, безусловно, не значит, что школа и спецификации не нужны. Очень нужны, и даже зачастую интересны. Но они все же навязывают готовое решение. А найденный дефект дает шанс совершить собственное открытие. Может за это мы и любим тестирование.

Удачи вам, удивительных дефектов и интересных открытий.

О тестировании документации


Documentation is like sex: when it is good,
it is very, very good; and when it is bad,
it is better than nothing.
Dick Brandon

 

Когда заходит речь о тестировании, обычно обсуждается тестирование приложения в том или ином виде. Функциональное тестирование, тестирование безопасности, нагрузочное тестирование, тестирование требований — все это относится к приложению. Это действительно важные аспекты поведения продукта, и количество уделяемого им внимания вполне обосновано. Тестирование документации, поставляемой вместе с приложением (неважно, каким именно способом — в виде книжки, PDF файла или базы знаний на сайте), нередко проводится по остаточному принципу. С одной стороны этот подход делает актуальной фразу вынесенную в эпиграф. С другой — соображения, высказанные в этой фразе могут стать причиной, почему документация так плоха.

Отбросим случай, когда документация не тестируется вообще никогда и никем. Это крайний и маловероятный вариант, к тому же обсуждать тут особо нечего. Как мы тестируем документацию, если уж до этого дошли руки?

Обычно документация не обновляется вся целиком. Появляются новые возможности в продукте, их описание добавляют в документацию, а потом назначенный человек (или несколько) проверяет, что «все в порядке». Это неплохой вариант, когда проверку выполняет опытный человек, хорошо знакомый с продуктом и прочим контекстом (предметной областью; языком, на котором написана документация; целевой аудиторией и так далее). Проблема в том, что часто нет никаких формальных подходов к выполнению таких проверок. Человек читает документацию, по сути — проверяя, не зацепится ли взгляд за что-то странное. Зацепился — хорошо: обсудил с окружающими, открыл баг. Не зацепился — никаких гарантий, что проблем действительно нет.

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

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

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

Важное умение, как и при тестировании приложения, — поставить себя на место пользователя, попытаться понять, чего он ждет от документации. Не от хорошей же жизни он полез читать эти талмуды. Одна из классических проблем документации: она написана программистами в привычной им манере. Программисты привыкли к описаниям того или иного языка программирования, когда для каждой функции описано, что она делает, какие параметры принимает на вход, что отдает на выход. Этот же подход переносится и в мануалы создаваемого продукта. Описаны параметры, которые можно установить, но непонятно, как это все работает вместе. Нередко пользователю нужно описание вариантов использования (use case) — что, где и в какой последовательности он должен сделать, чтобы получить нужный результат. Если вместо этого он увидит, пусть даже подробное, описание параметров, далеко не факт, что он сумеет достичь цели. Недаром на форумах так много вопросов, связанных с простейшими функциями приложений.

И все же основная проблема, на мой взгляд, связана с требованиями и критериями. Чего мы ждем от документации и по каким критериям мы решаем, есть дефекты в тексте или нет? Если требования и критерии не заданы, то можно (или придется) определять их в процессе тестирования. Главное, чтобы все заинтересованные стороны были с ними согласны.

А вообще, не преувеличиваю ли я проблему? Документация должна рассказать пользователю, что и как делает продукт. Чего тут непонятного? Садимся и пишем.

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

Верность и полнота информации. Тут все понятно. Все, что написано в мануалах, должно соответствовать действительности. По возможности, описание должно быть полным; пользователю может понадобиться информация о любой из возможностей продукта. Более того, если в документации не написано о каких-то возможностях, то пользователь может никогда о них и не узнать (разве что случайно наткнется на них, работая с приложением). Документация не должна содержать устаревшей информации. Возможны исключения, например описание API может включать упоминания устаревших функций с соответсвующими пометками. Но «в среднем по больнице» устаревшие и неактуальные данные о работе приложения, оставленные в документации, могут стоить пользователю кучу времени и денег.

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

Удобство навигации. В идеале пользователь вообще не должен задумываться, как найти нужные данные. Документация должна сама «привести» его к ним. Это, конечно, труднопредставимое счастье, но позаботьтесь хотя бы о понятных ссылках между частями; о структуре содержания; об алфавитном указателе. Если документация оформлена в виде PDF документа, и он содержит закладки (bookmarks), то проследите, чтоб панель с закладками показывалась сразу при открытии документа. Не всякий пользователь каждый день читает PDFки — облегчите ему жизнь. Вообще, подумайте, как вы сами ищете информацию в мануалах, и как этот процесс можно ускорить или упростить.

Соблюдение политик документации. Обычно в документации декларируются некие конвенции оформления текста. Названия кнопок мы пишем так, консольные команды по-другому, важные замечания — по-третьему и т.д. Если уж подобные правила введены, то стоит проследить, чтобы они действительно выполнялись. В противном случае документация как минимум выглядит неряшливо, а как максимум запутывает пользователя.

Структурированность. Легко ли взгляд находит важные места в документации? Если вы читали документ, отвернулись на пару секунд, а затем вернулись к документу — как быстро вы находите место, на котором остановились? Структурирован ли текст или выглядит сплошным массивом букв и знаков препинания? Это не очень простой пункт; многие чувствуют и могут заявить, что читать неудобно, но почему неудобно — сформулировать трудно. Тут важную роль играют шрифты, межстрочные интервалы и другие нюансы оформления. Если не получается найти причину неудобства, то можно попробовать хотя бы описать как вам неудобно читать, что за проблемы возникают при чтении. Это тоже полезная информация в данном случае.

Расшифровка терминов. Если документация содержит какие-то специфичные термины и аббривеатуры, то необходимо убедиться, что соответствующие определения и расшифровки тоже присутствуют.

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

Копирование текста. Тут я имею в виду случаи, когда пользователь хочет, например, скопировать кусок текста из документации. Это может быть какая-то информация, которую он хочет сохранить отдельно, или код скрипта, который ему нужно выполнить, или какой-то список, да мало ли что. Что произойдет при копировании? Сохранятся ли переносы между строками? Если копируемый текст размещался на нескольких страницах, были ли скопированы коллонтитулы страниц? Не содержит ли скопированный список непечатаемых символов? Не потерялись ли они при копировании? Можно ли вообще скопировать данные из документации?

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

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

Разделение ролей. Если продукт предусматривает различные роли и уровни доступа, и для каждого из них пишется отдельный документ, описывающий работу с системой, то стоит убедиться, что документ, предназначенный для одного уровня не содержит информации, позволяющий узнать что-то специфичное и важное о более привилегированном уровне. Например, рядовому пользователю необязательно знать, как конфигурируется система. Понятно, что если он сильно захочет, то скорее всего найдет соответствующий документ. Но, по крайней мере, случайно не наткнется. К тому же лишняя информация, не относящаяся к его уровню доступа, может его запутать.

Ого, уже одиннадцать пунктов. А казалось бы, всего лишь тестирование документации, ничего сложного.

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

Удачи в тестировании!

Правильный багрепорт 2: точность описания и исследование проблем

Разработчики любят понятные багрепорты, четко описывающие, в чем заключается проблема. С понятным и простым сценарием воспроизведения. С логами, относящимися к описываемой проблеме. С дополнительными данными, доказывающими, что дело не в кривой настройке окружения. Идеальный багрепорт — тот, прочитав который разработчик сразу знает, что нужно починить (по крайне мере на уровне бизнес логики). Идеал редко достижим, но можно к нему стремиться. Если разработчики редко жалуются на недостаток (или, наоборот, избыток) информации в багрепортах, то мы на верном пути.

В прошлом посте про качество багрепортов я писал, как не забыть упомянуть в описании проблемы важную информацию, которая в момент описания бага кажется очевидной (для тестировщика, находящегося в контексте собственных действий). Я тогда предлагал простое техническое решение, которое напоминало, что помимо самой проблемы нужно еще упомянуть ряд параметров окружения и ожидания тестировщика от поведения приложения. Однако подобным образом можно перечислить только небольшой набор самых общих параметров, упоминание которых необходимо, но зачастую недостаточно для исследования/понимания сути проблемы. Набор данных, которые стоит упомянуть в описании проблемы, разнится от проблемы к проблеме — где-то достаточно сделать скриншот, где-то нужно добавить логи, нередко и в базу данных заглянуть полезно. Как же понять, что писать, а что нет? Ведь избыток информации тоже может запутать разработчика.

Вообще, встретив какую-то странность в поведении приложения, тестировщик должен прежде всего понять, баг это или нет; а если баг — в чем он заключается. Возможно, просто так хитро звезды, в смысле — настройки приложения, совпали, что поведение немного изменилось. Или может проблема в чужом продукте, с которым работает наше приложение. А может приложение все сделало верно, просто в пользовательском интерфейсе сообщило об этом не самым адекватным способом. Все это не исключает наличия проблем в нашем продукте, просто проблемы уже немного другие: нигде не сказано, что при таком сочетании настроек приложение станет себя вести иначе; нам надо учитывать, что чужое приложение ломается на определенных входных данных; нам нужно улучшить отчеты приложения о выполненых действиях.

Важно докопаться до сути проблемы и акцентировать на ней внимание в багрепорте. Так, в случае особого поведения при специфических настройках, с точки зрения приложения все может быть верно: оно и должно так себя вести. И программист закроет баг как «инвалид» — ведь в программе проблемы нет. Однако в продукте проблема есть: документация должна явно сообщать о подобных зависимостях поведения приложения от настроек. Соответственно, если в багрепорте написано про нехватку описания работы программы, фокус багрепорта смещается с непосредственной работы приложения, и становится понятно, что хотя приложение и ведет себя правильно, баг продукта тем не менее присутствует.

Но как понять, в чем проблема? Как вообще догадаться, что нужно посмотреть в эти настройки? В сложных продуктах подобные зависимости могут быть не очень-то очевидными, и даже не упомянутыми в спецификациях. Подобные знания и чутье приходят с опытом — опытом «вообще» и опытом работы с данным конкретным продуктом. Но независимо от количества накопленной маны, полезно всегда помнить следующее: если ты сам не понимаешь, что происходит и почему, то скорее всего и описать проблему толком не сможешь. И прочитав подобный невнятный багрепорт, разработчик может понять его как-то по-своему и начать менять или исследовать нечто, о чем и вовсе речи не шло.

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

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

Кроме дополнительных вопросов, стоит поглядеть (если есть возможность), как разработчик исследует проблему. Обычно это несложно сделать, если вы уже пришли к разработчику с вопросом. Наблюдая за ним, можно почерпнуть много полезного — узнать расположение каких-то специфических лог-файлов, как их читать и как изменить настройки логирования; увидеть новые полезные утилиты и способы их использования. Возможно, даже увидеть в целом процесс исследования определенного класса проблем. Если запомнить подобные вещи, то в будущем можно подробнее исследовать проблемы самостоятельно, что позволит писать качественные багрепорты.

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

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