пятница, 6 сентября 2013 г.

Тестирование и проверка - перевод статьи Майкла Болтона.

У меня для вас очередной сюрприз - я представляю вашему вниманию свой перевод очень известной статьи Майкла Болтона "Тестирование и проверка" (Michael Bolton "Testing VS checking"). Работа оказалась довольно продолжительной, но очень интересной и насыщенной, богатой новыми впечатлениями и получением нового ценного опыта.

Большое спасибо моей сестре Лизе Ладутько - за помощь в переводе и придание ему более литературной формы :) Если вы не читали статью - рекомендую восполнить этот пробел прямо сейчас, если читали и знакомы - буду признателен за ваши отзывы - баг-репорты и улучшения, которые помогут сделать перевод качественнее.

Ссылка на оригинал  - http://www.developsense.com/blog/2009/08/testing-vs-checking/
Формат текста (выделение терминов, курсив, цитирование) и ссылки на другие статьи из текста взяты с оригинала "как есть".

Эта статья раскрывает тему моего блиц-доклада на конференции Agile 2009. Я выражаю огромную благодарность Арлу Бельши (Arlo Belshee) и Джеймсу Шору(James Shore) за помощь в воплощении идеи в реальность. Также я хочу сказать «большое спасибо» программистам и тестировщикам за огромную поддержку моего доклада и моей идеи. Особую благодарность я выражаю Джо Рейнсбергеру (Joe (J.B.) Rainsberger). Распространите этот мем!

P.S. На протяжении многих лет люди неверно истолковывали эту статью как отказ или от проверки, или от регрессионного тестирования, или от автоматизированного тестирования. Вдобавок к этой статье, для лучшего понимания вам также необходимо прочитать (http://www.developsense.com/blog/2009/11/merely-checking-or-merely-testing/ ).

P.P.S. С момента публикации прошло довольно много времени, этот пост достаточно старый. В течение последующих нескольких лет после того, как он был опубликован, я дополнил мои исследования данной темы, в основном благодаря сотрудничеству с Джеймсом Бахом. Наши размышления на эту тему появились в блоге Джеймса, а я разместил свой ответ здесь. В усовершенствовании процессов нам помогли вопросы и комментарии коллег. Мы просим вас прочитать их комментарии и оставить свои (Апрель 2013).

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

Проверка – это подтверждение.


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

Тестирование - это исследование и изучение.


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

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


Проверка даёт бинарный результат – «истинно» или «ложно», «да» или «нет». Проверка задаёт и в то же время отвечает на вопрос «Это утверждение истинно или ложно?». Для этих простых утверждений достаточно использование компьютера, и такие утверждения нейтрально оценочные.

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

Когда проходит проверка, мы не знаем, работает ли программа, мы знаем только то, что она работает в границах наших ожиданий. В программе могут быть серьёзные проблемы даже если проверка успешна. Перефразируя Дейкстру, можно сказать, что "проверка может показать наличие ошибок, но не их отсутствие". Машины могут распознать несоответствия и проблемы, на которые они были запрограммированы для распознавания, но не новые проблемы. Тестирование также не отвечает на вопрос, работает ли эта программа – оно не способно отвечать на такие вопросы – но тестирование может давать основу для весомого заключения для ответа на вопрос: «Есть проблема или ее нет?».

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

Будем ли мы автоматизировать процесс или нет, если бы мы могли сформулировать вопрос так, чтобы машина смогла задать и ответить  на него через утверждение, это почти точно будет проверка. Если же для проведения этой работы необходим человек, если эта работа интеллектуального характера, это, скорее всего, тестирование. В самом начале своей статьи  «Разумные процессы», Джеймс Бах сказал «Я занимаюсь тестированием программного обеспечения. Я слышал от многих людей, что они занимаются тем же делом, что и я. Иногда, после того, как эти люди говорят об автоматизации тестов, мне начинает казаться, что они занимаются не тем же делом, которым занимаюсь я. И это так, потому что, на мой взгляд, то, что делаю я, невозможно автоматизировать в самом полном значении этой фразы. И я спрашиваю сам себя: «Какого черта они что-то автоматизируют?!». И тут же отвечаю «Они автоматизируют проверки».

Когда мы говорим о «тестах» на каком бы то ни было уровне, в которых мы делегируем решение "Да или Нет" машине, мы говорим об автоматизированных проверках. Я предлагаю называть «модульные тесты» "модульными проверками". По этой же причине, я предполагаю, что автоматизированное приемочное тестирование (Рон Джеффрис ссылается в своём блоге в статье Автоматизация историй «тестами») должно быть известно как "автоматизированные приемочные проверки". Это предложение появилось, чтобы оживить группу опытных программистов и тестировщиков на воркшопе конференции Agile 2009, о чем я расскажу вам поподробнее в следующем посте блога.

Тестирование - это не контроль качество, проверка - может быть.


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

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

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

Проверщикам нужна спецификация, тестировщикам - нет.


Тестировщик, по словам Джерри Вайнберга, это "кто-то, кто знает, что вещи могут отличаться" (things can be different (c)). Как тестировщики, наша работа - поиск инорфмации, которая зачастую представляет разницу между тем, как думают люди и тем, что верно на самом деле. (определение тестирования Сэма Канера включает в себя вышесказанное: "Тестирование - это эмирическое, техническое исследование продукта, выполняемое заинтересованными сторонами с целью выявления информации про качество такого типа, который они ищут".)

Мы часто слышим от оппонентов "старой закалки", что для хорошего тестирования нужны четкие, полные, актуальные и однозначные спецификации. (Я люблю спрашивать таких людей "Что вы понимаете под "однозначностью"?" Они редко понимают, что это шутка. Но я отвлекся.) Тестировщику не нужно быть уверенным в совершенстве спецификации для того, чтобы сделать полезные наблюдения и выводы о продукте. В самом деле, задачей тестировщика может быть сбор информации, которая выявляет слабости и неоднозначности в спецификации, с целью предоставить информацию людям, которые могут объяснить ее. Отчасти роль тестировщика может заключаться в выявлении проблем, связанных с тем, что планирование продукта расходится с его реализацией, даже если планирование не было описано. Задачей тестировщика может быть обнаружение проблем, которые возникают, когда наш замечательный код вызывает код с ошибками в сторонней библиотеке, для которой у нас нет спецификации. Опытные тестировщики могут справиться в этой ситуации.

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

Тестирование VS Проверка - это дырявая абстракция.


Джоэл Спольски назвал закон о ценных движениях системы Законом Дырявых Абстракций ("Все нетривиальные абстракции, в некоторой степени, дырявые.") В процессе разработки продукта мы можем быстро переключаться между тестированием и проверкой. Различие между ними находится непосредственно в нашей мотивации. Давайте рассмотрим несколько примеров.
  • Программист, который пишет новый код и исследует новое проблемное поле. В его понимании, есть ситуация, которую нужно обработать. Он пишет утверждение - проверку. Затем он пишет код, чтобы утверждение прошло успешно. Утверждение не проходит успешно, тогда разработчик переписывает код. Проверка все равно не проходит. Программист обнаруживает, что его первоначальное предположение о проблеме было неполным, поэтому он меняет утверждение, и дописывает код. В этот раз проверка проходит успешно, что означает, что утверждение и код приложения согласованы. У него появляется идея - дописать еще код, и он снова повторяет свой алгоритм - сначала пишет проверку, затем - код, для того, чтобы проверка прошла успешно. Он также удостоверяется в том, что предыдущая проверка также работает. Затем, он видит, как код может "упасть", если ввести другое значение. Он уверен, что код пройдет успешно, но он пишет новую проверку, чтобы удостовериться в этом. Проверка проходит успешно. Он пробует другие входные значения - тест падает, и программист вынужден исследовать проблему. Он сознает свою ошибку, и использует полученное знание для новой проверки; затем он пишет функциональный код, чтобы исправить проблему и чтобы проверка прошла успешно. Таким образом, данный процесс - в значительной мере, исследование. Несмотря на то, что она продолжает использовать проверки для продолжения процесса, его фокус направлен на изучение, исследование проблемного поля, обнаружение проблем в коде и разбор  этих проблем. В этом смысле, он тестирует и программирует. В конце этого всплеска разработки, он получил работающий код, который пойдет в версию продукта. В качестве положительного побочного эффекта, у программиста появился кусок кода, который поможет ему выполнить автоматические проверки, если функциональный код будет изменен. Марк Симпсон, программист, с которым я беседовал на конференции Agile 2008, сравнил этот циклический процесс с расчисткой тропы в зарослях, прорубанием нового пути через проблемное поле. Есть множество путей, по которым ты можешь пойти, и ты расчищаешь заросли неопределенности вокруг себя в попытке добраться туда, куда направляешься. Исторически, этот процесс называется "разработка через тесты" (Test Driven Development, TDD),  с маленькой поправкой - в разработке через тесты, "тесты" - на самом деле, проверки. Хотя это может быть трудно, и даже несправедливо, доказывать, что в общем-то, весь этот процесс, в большей мере, не исследование. У программистов, работающих по TDD, есть цель, но путь к этой цели не всегда очевиден. Если вы знаете точно, куда идти и как вы намереваетесь идти, вам придется исследовать.  Название "разработка через поведение" (Behaviour Driven Development, BDD) отчасти наводит порядок в этой неразберихе, но оно еще не получило широкое распространение. Разработка через поведение использует проверки в форме "(Программа) должна...", но процессу разработки необходимо тестирование идей по мере их формирования.
  • Затем наш программист просматривает код и обнаруживает, что имя одной из переменных не "говорящее", что одна строка кода должна быть более читабельна и сопровождаема, если ее переписать в две строки, что группа из трех строк можно переписать более красиво и понятно, используя цикл. Он решает отрефакторить код и обращается к решению этой проблемы - запустить проверки после каждого изменения. Его цель при запуске этих проверок - не исследование, а подтверждение, что ничего не было сломано. Он не пишет новые проверки, он вполне уверен, что старые справятся с этой задачей. В этом случае, она не тестирует продукт, она проверяет свою работу.
  • Большинство книг по традиционному "тестированию" утверждает, что "тестирование" это процесс валидации и верификации, как будто мы уже заранее знаем, как должен работать код. Хотя тестирование включает в себя проверку, программа, на которой были выполнены только проверки, скорее всего, недостаточно протестирована. Большинство литературы по тестированию фокусирует внимание на корректности - которая может быть проверена - и игнорирует тот факт, что необходимо задавать более глубокие вопросы о значениях, которые должны быть протестированы. Например, то, что называется "тестирование граничных значений" - обычная "проверка граничных значений". Классический пример программы, которая проверяет сложение двух двузначных целых чисел, в котором значения находятся в допустимом интервале от -99 до 99, а все значения вне пределов интервала отбрасываются. Классический совет, как "протестировать" такую программу, направлен на граничные условия, звучит как "Попробуйте -99 и 99 чтобы убедиться, что допустимые значения обрабатываются, и попробуйте -100 и 100, чтобы проверить, что недопустимые значения не обрабатываются. " Я бы хотел поспорить, что эти "тесты" настолько слабы, поэтому должны называться "проверками"; они крайне очевидны, они направлены на подтверждение, они направлены на исход, а не на результат, и могут быть легко автоматизированы. Если вы хотите протестировать подобную программу, сам следует настроить, поработать, рассмотреть продукт внимательно, с учетом и других рисков, включая не совсем очевидные, которые могут оставаться незамеченными, пока не проявят себя. Вы должны быть готовы рассмотреть все, что может повлиять на ценность продукт - проблемы, связанные с производительностью, инсталляцией, удобством использования, тестируемостью и другими критериями качества. Вам следует изменять свои тесты, а не повторять их. Вам следует быть любознательными, и выполнять некоторые тесты, не связанные с вашей текущей моделью рисков и угроз, с целью обнаружить непредвиденные риски. Вы можете использовать автоматизацию как помощник в исследовании; например, для генерации данных, для отслеживания покрытия, для разбора лог-файлов, просматривать реестр файловой системы для поиска непредвиденных последствий. Даже если вы используете автоматизацию с целью нажимать клавиши за вас, вам следует использовать автоматизацию для исследования, вы должны быть готовы изменять направление исследований и тактику, когда тест вам выдает неожиданный результат.
  • Исследовательское мышление использует вопросы наподобие "Что, если...?", "Мне интересно, ...?", "Как эта вещь...?", "Что произойдет, если я...?" Даже если мы тестируем программу, используя строгий подход к исследованию, мы дополнительно задействуем новые идеи, как это должно работать. "Если я нажму кнопку Отмена, диалоговое окно закроется", "Это поле запрашивает ЗИП-код в США, следовательно, это поле должно позволять вводить как минимум 5 символов", "При двойном клике по "foo.doc", файл должен открыться в программе Microsoft Word". Тестировщики-профессионалы использует эти и другие утверждения как сами собой разумеющиеся. Мы даже можем их сознательно не называть проверками, но мы подсознательно проверяем во время своего исследования и изучения программы. Если одна из проверок выполнится с ошибкой, мы наверняка получим подсказку в поиске новой информации, или если поведение программы кажется правильным, мы изменим наше представление о том, как программа должна работать. Это эвристический процесс (подверженный ошибкам означает, что при решении проблем или принятии решений, мы обучаемся, и эвристика обычно работает, но может и не работать).
  • Также на конференции Agile 2009, Крис МакМахон показал презентацию "История большого проекта автоматизации с помощью Selenium". Он описал подход с использованием тысяч автоматизированных проверок (он называл их "тестами") с целью обнаружить проблемы в приложении с помощью тестирования. Как описать разницу? Опять-таки, различие - это одна из мотиваций. Если вы запускаете тысячи автоматизированных проверок с целью показать, что у вас сегодня все работает, как и вчера, вы проверяете. Хотя, вы можете использовать тысячи автоматизированных проверок другими способами. Если вы пытаетесь ответить на новые вопросы, например "что случится, если мы запустим наши проверки на 30 машинах одновременно, чтобы нагрузить сервер?" (мы выполним стресс-тест), или "что может случиться, если мы запустим все проверки на другой платформе?" (мы выполним тестирование совместимости), или "что будет, если мы запустим автоматизированные проверки 300 раз подряд?" (тестирование последовательности), проверки помогут нам в тестировании (что будет замечательно в этих ситуациях).
Можно еще долго, очень долго рассказывать про тестирование и проверки. Я гарантирую, что это различие не будет принято полностью, и никто не будет заниматься этим ночи напролет. Но я призываю вас обратить внимания на эти различия, и явно различать эти понятия там, где вы сможете это сделать.

Майкл Болтон, 29 августа 2009 года.

1 комментарий:

  1. "Как тестировщики, наша работа - поиск инорфмации," - информации

    ОтветитьУдалить