Id. - Motion and revision. Тест новой функциональности.
Дек. 23, 2007
05:30 am - Motion and revision. Тест новой функциональности.
Тестируя софт десятый год, я сталкиваюсь с некоторыми стереотипными представлениями об этой деятельности со стороны менеджмента и других коллег смежных профессий. Межцеховые предрассудки появились не в IT. Профессор и его шофёр, столяр и плотник безотчётно подразумевают друг о друге странные вещи. Набоков удивлялся популярным фильмам, где писатели начисто настукивают текст на печатающих машинках. А ведь наверняка многие уверены, что так и есть: литератор заправляет бумагу, присаживается, и тут же в один вечер, кажется, все написал, всех изумил.
Обычно заблуждения не предают огласке. Но иногда о тебе не только думают, что ты ешь живых гусениц, но с ритуальными поклонами пытаются живыми гусеницами кормить. Вот подобранный на вскидку список того, что лично я не ем. Но в ту или иную пору добрейшие люди на управляющих должностях мне это подавали. Некоторые даже управляли тестировщиками. Я тоже так называюсь, но видимо, в каком-то параллельном мире.
1. Программы не существует, есть только её описание ("спецификация"). Предлагается составить тест.
2. Тестирование программы недавно началось. Задаётся вопрос, сколько оно займёт, если судить по существующим тестам.
3. Предлагается составить тест и отдать его для выполнения человеку, доселе не знакомому с программой.
4. Предлагается покрыть функциональность программы тестами в сценарной форме. "Запустите программу, откройте проект, откройте файл..." И так пять тысяч раз.
5. Предлагается приложить специальные усилия, чтобы разные тесты ни в коем случае не обнаруживали бы невзначай одну и ту же ошибку.
Здесь везде речь идёт прежде всего о функциональных тестах, то есть проверке того, что в на любые введённые данные программа ответит правильно и понятно. "Данные" понимаются широко, включая воздействия пользователя на элементы графического интерфейса.
Завидев эти предложения, я только руками разводил. Чтобы объяснить мой скепсис на рациональном уровне, необходимо прежде всего представить себе процесс тестирования новой программной функции. Его дополнение - regression test я описывал раньше. Здесь же применяются два основных метода, из которых посторонний человек часто помнит самое большее об одном.
Некоторые представляют себе процесс составления теста сугубо аналитическим. Тестировщик осматривает программу (или её описание) и делает пометки на бумажке. Потом он глубоко задумывается над пометками, и вот - готов список проверок.
Ключевая особенность такого представления не в том, что тестировщик не пробует, как ведёт себя программа. Это допускается. Заблуждение состоит в том, что ответ программы будто бы не имеет значения. Тестировщик будто бы листает программу, как книгу, и состав теста зависит только от его внимательности и вдумчивости. Вот панель. Вот окно. Вот опция. Мы это всё аккуратно переписали, даже, может быть, потыкали в кнопочку, что-то заработало, что-то не заработало, но как бы то ни было, после нашего прохода состав теста в целом ясен. Содержание теста, так сказать, "линейно зависит" от суммы наших наблюдений.
Второй метод - ролевой. Тестировщик пытается решить с помощью программы те задачи, для которых она предназначена, и рапортует о помехах. Решением задачи является сценарий, состоящий из поочерёдных обращений к различным функциональным блокам программы. Без такого подхода, конечно, никуда. Но следует помнить, что пользователей программы - больше, чем её создателей, и у каждого есть свои предпочтения. Это значит, что там, где обычный пользователь введёт те единственные данные, которые ему лично интересны, тестировщик должен будет ввести десятки разнообразных наборов таких данных. Если в сценарии участвуют пять-шесть функциональных блоков (а это очень мало), то выходит, что необходимо выполнить где-то десять в пятой-шестой степени сценариев, что является непосильной работой. Поэтому приходится (сделаем вид, что скорбим) отказываться от сценарного подхода и анализировать каждый функциональный блок: какие в нём, отдельно взятом, есть входы и выходы. А это уже не ролевой подход.
Синтез двух методов, который используется в настоящей работе, - итеративный процесс, где вы переходите от одного метода к другому. Если в разработке участвует серьёзный продукт менеджер, то пользовательские сценарии у вас уже есть, и вы можете начать с ролевого подхода. Я такого счастья не видел давно, и в своё время мы рассказали друг другу немало анекдотов о нашем продукт менеджменте. Всё это, однако, не имеет большого значения, сценарий менеджера - это, так сказать, минус первый шаг, подземный этаж. Рано или поздно, то есть где-то во втором часу работы вы останавливаетесь у первого функционального блока, принимающего несколько строк, одно булевское и одно целое значения. Тут, вне зависимости от того, с чего вы начали, происходит самое интересное.
Сперва вы, конечно, действуете аналитически. Прикидываете классы эквивалентности и необходимые комбинации значений, набрасываете тест: слева - кто говорит, справа - что говорит. Затем решаете попробовать, ну, скажем, пятую строчку. Для этого необходимо выполнить подготовительную работу: создать проект с такими-то свойствами, открыть такие-то панельки. Секунду... Стоп, что происходит? Я вообще не думал, что это возможно! Действия, назначение которых состояло в скромной подготовке входных данных, внезапно обнаружили не предугаданный ранее дефект, при том, что условий его воспроизведения в плане вообще не было - только чистое поле и два просёлка.
Четверть часа скачки по полям, и ты оформляешь поимку. Затем возвращаешься ещё раз, потому что, гоняясь за одной ошибкой, заметил ещё две. Когда все переписаны, следует включить в план несколько новых и неожиданных тем, про которые ещё нужно подумать, в какие проверки их раскрывать. К вечеру, наконец, наступает затишье, а исходный состав теста сильно изменился. С некоторой скукой можно дотестировать первый функциональный блок. Конечно, программист забыл про отрицательные числа и сохраняет пароль, не шифруя, но это общие места, спустя годы они даже не веселят.
Дальнейшее продвижение носит тот же характер. Марш по главной улице, неожиданное происшествие, бешеная беготня по подворотням, возвращение на изменённый уже маршрут, и снова шагом. Изменения, которые вносит программист, дополнительно увеличивают весь цикл. Это может показаться странным, но пресловутые user scenario от самого лучшего продукт менеджмента не предоставляют такого богатого и полезного экспериментального материала, как твои сугубо подготовительные, промежуточные действия между тестами. Ценнейший опыт использования программы, не описанный ни в каких сценариях, возникает, когда нужно прибрать вот тот бардак, перебросить все объекты на одну сторону и схлопнуть дерево.
В это время действия тестировщика характеризуют два важнейших интегральных качества. Первое я называю термином, заимствованным из спортивных игр: "движение". В нашем случае его можно грубо определить, как количество и разнообразие входных данных, подаваемых в программу. Помню об одном креше, случавшимся в Together на sequence diagram, если указатель мыши определённым образом пересекал изображение одного из элементов. Тестер, обнаруживший проблему, знал, что она существует, но, не сумев определить условия воспроизведения с двух раз, отчаялся и просто смотрел на экран, сложив руки. Проведав о его беде, я стал создавать одну за другой почти одинаковые диаграммы, отличающиеся мелкими подробностями. Первую, вторую, третью, десятую... Со стороны казалось, что я тронулся. После двадцати экспериментов я увидел креш, и после ещё двадцати определил точный рецепт его получения. Мораль истории - не в том, что нужно много работать. Просто те, кто стремится действовать сугубо аналитическим методом, психологически не готовы к неожиданностям. Им страшно, хочется уйти в сторону. Как же так, этого же не было в плане! Сказывается и общая для всех пользователей "боязнь сломавшейся программы". Когда я просматривал кандидатов, претендующих на рабочее место в моей бывшей конторе, их "движение" при выполнении тестового задания сильно влияло на мой выбор.
Второе качество - "пересмотр теста", то бишь умение и моральная готовность выполнять эту процедуру. Когда все неожиданности рассмотрены, тестировщик должен вернуться к наброскам своего плана и убедиться, что в нём действительно предложены входные данные, приводящие ко вновь обнаруженным ошибкам. Инстинктивно хочется ответить утвердительно - ведь план выглядит так красиво. Но красота эта ложная, правильный ответ чаще отрицательный. По-хорошему, с каждой не предсказанной ошибкой план следует менять. Это не только добавление новых разделов, но и переделка небрежно составленных старых. Утрируя, можно сказать, что ценность тестера не в том, как аккуратно он следует плану, а в том, как он его нарушает.
Откуда я взял, что нельзя предусмотреть всё в самом начале? Из опыта. Сколько ни наблюдаю за этой работой, всегда бывает так. Носит ли явление силу закона? Возможно ли сделать тестирование более предсказуемым? Ведь O(n) лучше, чем O(неизвестночто), верно? Ну, как сказать, как сказать. Тут - несколько соображений.
Во-первых, если состав рассматриваемых функциональных блоков непредсказуемо изменяется, то возможно, это дефект образа мышления, подхода к декомпозиции функциональности программ. Мы мысленно делим их горизонталями, а надо, может быть, - в колечко. Тогда и ясность наступит быстрее. Пока ни у меня, ни у лучших из моих коллег она так быстро не наступает, и тут мне нечего добавить.
Во-вторых, напрашивается идея о неких шаблонах тестирования (test pattern) с заранее готовым набором проверок. Так сказать, "проверки, которые стыдно забыть" (ПКСЗ). Например, в мире существуют сотни программ с текстовыми редакторами, в которых реализована синтаксическая подсветка. Для всех этих программ имеют смысл характерные проверки вроде "удалить символ из лексемы, проверить изменение подсветки". И ещё много подобных. Стало быть, имеем шаблон "Редактор с подсветкой", к которому можно всем миром насочинять полезных проверок и везде их применять. Можно будет на ранних стадиях точнее оценивать затраты на фичу "снизу": достаточно перечислить шаблоны, которым она соответствует. Возможно, такое где-то уже есть, я об этом не слышал.
В-третьих, для того, чтобы так или иначе проводить функциональную декомпозицию, недурно во всей полноте представлять себе функции программы. А эти функции - не всегда феномен спецификации или технического задания. Рефакторинг в специфическом случае может падать исключительно на переменных классов из java.lang, например. Или классов-потомков java.awt.Component. На особое отношение к ним будет веская внутренняя причина, но программист вспомнит о ней только тогда, когда вы наткнётесь на этот баг.
В-четвёртых, о принципиальной постановке задачи. К чему мы стремимся? Различные технологии борьбы за качество и управления процессом разработки явно или неявно стремятся исключить фактор неожиданности, сделать происходящее как можно более управляемым. Чтобы без сюрпризов. Но хорошо ли это для проверки новой фичи? Допустим, мы проникли взглядом в камень, применили test patterns, и в самом начале тестирования получили готовенький набор из многих проверок. Теперь любой дурак сможет протестировать новую функцию. И что же выйдет? Выйдет то, что с одной стороны, именно дураков для этой цели и станут нанимать. Это ведь дёшево. А с другой стороны, дураки, отбарабанив автоматически созданный тест, немедленно, без сомнений сообщат менеджеру, что всё сделано. И те сотни уникальных ошибок, которые есть в любой программе - в силу её собственной уникальности - останутся незамеченными. Ведь ежедневный пересмотр теста исполнители такого сорта делать не станут.
Выявление непредсказуемых ошибок - это, может быть, и зло для планирования. Но постольку, поскольку могу судить, нет лучше средства проверить новую фичу, чем пройти через все эти неприятности. Я также не против применения чего-либо вроде шаблонов тестирования, но - не в начале, а в конце работы. То есть, сперва мы рассматриваем программу как уникум, и только когда план перестаёт геометрически расти, сверяемся с шаблонами - не упущено ли чего.
Теперь - к обещанным в начале стереотипам.
1. Программы не существует, есть только её описание ("спецификация"). Предлагается составить тест.
Нет программы - нет ролевого подхода и неожиданных ошибок. Составленный тест, таким образом, будет находиться на первой итерации своего развития, дальше не продвинуться. Настоящая работа начнётся, когда тестер получит прототип.
2. Тестирование программы недавно началось. Задаётся вопрос, сколько оно займёт, если судить по существующим тестам.
Судить о необходимых объёмах тестирования можно, когда план перестаёт расти. То есть тестировщик попробовал хотя бы одну-две проверки из каждой изобретённой им темы, и не столкнулся с новыми неожиданностями. Если в данный момент всё ещё ловят чертей с рогами, то оценка оставшихся сроков работы - прерогатива оккультизма.
3. Предлагается составить тест и отдать его для выполнения человеку, доселе не знакомому с программой.
Сочинение тестового плана само собой выполняется совместно с первым тестированием. Если предложение касается теста новой фичи, оно бессмысленно. Если речь идёт о регрессионном тестировании, здесь есть о чём поговорить. Например о компетентности будущего исполнителя. Не легче ли будет автору теста сделать всё самому, чем объяснять важные нюансы постороннему. То же касается "автоматизации" тестов. Тут есть выбор.
4. Предлагается покрыть функциональность программы тестами в сценарной форме. "Запустите программу, откройте проект, откройте файл..." И так пять тысяч раз.
Формы тестовых документов - мой конёк, а в этом посте уже кончается место. Коротко говоря, полезны сценарии или вредны, они обладают интересным свойством: их тяжело переделывать. Связность повествования выдвигает дополнительные требования к редактуре. Если менеджер требует от тестировщиков сценариев в самом начале разработки, и подразумевается, что сценарии должны охватывать всю функциональность (то есть это не sanity-acceptance), значит, он своими руками задерживает работу или провоцирует саботаж.
5. Предлагается приложить специальные усилия, чтобы разные тесты ни в коем случае не обнаруживали бы невзначай одну и ту же ошибку.
Есть такой фетиш в популярной литературе. Читаешь какое-нибудь "Введение в основы теста" и вдруг на первой же странице в скобках крупным шрифтом предостережение: "Дети! Ни в коем случае! Не пишите различных тестов, в которых встречаются одинаковые проверки!" Сам удивляюсь, чего это они. В зависимости от случая предписание бывает верным или нет, второе - чаще. Вспомнил о нём для того, чтобы подчеркнуть: в обращении с новой функцией действует обратный принцип. Если различные темы теста по-разному испытывают один и тот же блок, и поэтому их проверки отчасти пересекаются, это не плохо, а хорошо. Motion, motion, motion! Подвергнув программу похожим испытаниям с различной предысторией, вы встретите больше неожиданностей или наоборот, удостоверитесь, что неожиданностей нет.
Вообще, тестовые документы соотносятся с настоящим содержанием проверок, выполненных профессионалом, как комментарии к коду соотносятся с кодом. Иногда они просто врут или умалчивают о самых важных, нетривиальных вещах. Известное дело: документация не успевает за своим предметом.
Если софт является заказным проектом, доходы от его использования получает заказчик. Соответственно он и несет риск от ошибок софта. Наши же доходы грубо говоря бинарны: если заказчик подпишет протокол приемки, деньги будут. Иначе их не будет. В этих условиях тестеры фирмы-исполнителя обычно используются для оформления протокола приемки. Ну, не придумано еще ничего лучше для процедуры сдачи проекта, чем дефинировать формальный и конечный набор сценариев и функций и протестировать их икс-процентное прохождение.
Т.е. в продуктах тестеры производят научно-исследовательскую работу, а в заказных проектах - обычную ОТК. Удивительно, почему они тем не менее одним и тем же словом обозначаются...
Скажем шаблоны тестирования текстового поля, числового и наверное есть что-то более серьезное, но при этом типовое.
Меня же интересовали более высокоуровневые проверки, в которых объектом выступало бы не текстовое поле, а, например, редактор кода, или модель - древесная структура. Таких шаблонов я не видел. Может, тот же grundik сталкивался.
По сути я всё написал выше в комментарии. Известный мне реестр - только у Канера.
Да только у него общие принцыпы про граничные условия и т.п. Эх, хотелось готовенького, а придется своей головой думать:)
Стандартный для маков Ctrl+Click не работает в идее - а на ноуте с одной кнопкой мыши это действительно напрягает
Кстати, с учётом тенденции нежелания переходить на висту, много народу может повалить на маки и линухи... И тут уж одним тестером не обойтись будет =(
А будет ли мне за это официальная персональная лицензия? ;-)
Один из крэшей происходит при попытке запуска плагина, собранного под JavaSE 6, а на маке последняя официальная версия - Java 5. Причём сначала крэшится идея, а на второй запуск - сам JDK.
Плагин Scala вроде крэшнул всё.