Разделы портала

Онлайн-тренинги

.
Как один тестировщик учился программировать
15.12.2009 23:27

Автор: Прадип Саундараджан
Оригинал: http://testertested.blogspot.com/2009/12/why-testers-need-to-learn-to-write-code.html

Предисловие Алексея Баранцева

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

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

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

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

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

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

А пока я передаю слово Прадипу.

Как один тестировщик учился программировать

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

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

Мой любимый язык программирования Perl (вообще-то я просто не пробовал другие), я полюбил его с тех пор, как впервые «распробовал» его вкус, автоматизировав какие-то проверки на моей первой работе.

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

Чтобы на практике попробовать те возможности Perl, которые я изучаю, я решил делать упражнения, головоломки и игры для тестировщиков. Таким способом я собираюсь сделать свой процесс изучения Perl более увлекательным. Perl сам по себе весьма забавный язык, а представьте, каково будет его изучать, если я ещё добавлю «фана»?

Ну что, хотите посмотреть на головоломку, которую я приготовил для вас? Подождите, не торопитесь.

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

Тестировщик программирующий

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

Я использовал Komodo Edit для написания и редактирования кода, и мне нравится эта среда за её простоту. Я сохранял скрипты, присваивая им номера версий, когда считал, что внёс существенные улучшения в код. В одной из версий, экспериментируя с новой идеей, я допустил ошибку, в результате чего скрипт не мог даже просто напечатать "Hello World!". Я не отслеживал изменения, произошедшие в новой версии по сравнению с предыдущей. Я делал diff, но в этот момент внезапно осознал, что мне нужно нечто большее, чем простой diff.

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

А вообще, написание кода – это занятие, с точки зрения тестировщика не требующее большого интеллекта. Пришёл, увидел, написал. (Да, вот, так, всё просто).

Тестировщик, тестирующий написанное им самим

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

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

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

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

Тестировщик, исправляющий баги в процессе их исследования

Исправление багов – ещё одна важная штука, которую каждый тестировщик должен попробовать. Это помогает понять, что программисты действительно по-честному пытаются исправлять баги, но иногда это приводит к совершенно противоположному результату. Ещё это помогает понять, что стандартные тест-кейсы нисколечко не помогают в обнаружении этих случайно получившихся «противоположных результатов», так что обязательно нужно использовать технику тестирования методом свободного поиска, чтобы убедиться, что баг действительно исправлен. Если тестировщик сообщил о симптомах проблемы, а после исправления эти симптомы больше не проявляются, это ещё не означает, что проблема устранена. Так что нужно быть очень внимательным и различать симптомы и причины.

Давайте рассмотрим простой пример из моего кода:

if (($x == "\n") || ($x == "\t") || ($x eq ""))
{
     print "That's not valid according to me! Please try a number";
     &ask_for_a_number_or_reject_input ;  # sub routine call to seek a number from the user
}

Как вы думаете, что согласно моим ожиданиям должен был делать этот фрагмент кода?

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

Но когда я запустил программу, я с удивлением заметил, что иногда она вообще не заходит в этот цикл. Когда я видел приглашение ввести данные, я нажимал клавишу Enter, не вводя ничего, и надеялся, что увижу, как на экране появляется сообщение "That's not valid according to me! Please try a number", а потом вызывается подпрограмма ask_for_a_number_or_reject_input. Но сообщение не появлялось, и я никак не мог понять, в чем проблема. В конце концов выяснилось, что в этом случае переменная принимала значение "0".

В чем же была суть проблемы и как следовало бы написать баг-репорт?

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

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

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

Тестировщик, сопровождающий код

Я написал кода не пару тысяч строк, а всего несколько десятков. Ну да, если кто реально хорошо разбирается в Perl, он бы конечно написал ещё короче. Ладно, я тоже научусь, дайте срок :)

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

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

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

Не пора ли уже показать вам мою программу?

  • Это головоломка.
  • Ваша задача - провести реверс-инжениринг и понять логику программы.
  • Когда вы найдёте "Nemo", не радуйтесь слишком сильно, это только малая часть головоломки.
  • Найти Nemo очень легко, но главная сложность заключается именно в том, чтобы провести реверс-инжениринг.
  • Пожалуйста, отправляйте решения головоломки или просто ваши идеи на адрес pradeep  [/dot] srajan [/at] Jee mail [/dot] com. Договорились?
  • Важно даже не столько само решение головоломки, сколько ваш рассказ о том, каким путем вы её решали, как вы рассуждали при этом.

Загрузить головоломку (это exe-файл). Если в вашей организации запрещена загрузка exe-файлов, можно загрузить xex-файл (который потом, конечно, надо переименовать в exe-файл). А может быть у вас Mac? Упс... Тогда у меня для вас пока ничего нет :(

Итак, вперед, найдите Nemo и расскажите, как вы решали головоломку, ну или можете, конечно, ничего этого не делать, если у вас и так много работы Smile

Послесловие Алексея Баранцева

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

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