Уровни тестирования

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

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

Нет стандартных определений для каждого вида тестирования (unit, модульное, компонентное), но обычно тестирование кода называется unit-тестированием, т. е. тесты строятся для небольших кусочков кода. Оно происходит с возможностью просмотра самого кода в какой-нибудь среде разработки и с использованием инструментов автоматизации. Обычно такое тестирование проводят разработчики: либо тот, кто писал код, либо напарник. И дефекты обычно исправляются без заведения баг-репортов, по принципу “нашёл-пофиксил”. Модульным тестированием считается тестирование отдельных классов в коде. А компонентное тестирование направлено на отдельные функциональные компоненты программного приложения. Такие небольшие тесты помогают повысить чистоту кода, они быстро работают и быстро находят дефекты, которые легко локализовать.

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

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

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

На уровне выше находится интеграционное тестирование и оно занимается проверкой взаимодействия компонентов системы как между собой, так и взаимодействие компонента с другими системами. Это называется компонентным интеграционным тестированием.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Приёмочное тестирование можно разделить на два вида: пользовательское приёмочное тестирование (user acceptance test) и операционное или продуктовое приемочное тестирование (operational/production acceptance testing). Первое проверяет, подходит ли продукт для бизнес-пользования. Второй проверяет, соответствует ли продукт техническим требованиям, таким как производительность системы, загрузка данных, восстановление после полного сбоя системы, резервное копирование.

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

Для продуктов, которые разрабатываются для огромного количества пользователей, существуют два других типа приёмочного тестирования: alpha- и beta-тестирование. Сначала проводится alpha-тестирование. Для него все условия предоставляет компания-разработчик, и оно проводится группой независимых экспертов. Beta-тестирование проводится потенциальными и существующими пользователями продукта на их устройствах. Цель такого тестирования — воспроизвести реальные случаи использования продукта, убедиться в удобности продукта, исправить обнаруженные дефекты, которые могут возникнуть в различных условиях, например на разных устройствах и разных операционных системах, до начала массового пользования продукта.

На этом часть про уровни тестирования заканчивается.

Типы тестирования

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

Так вот, для каждой такой цели существует свой тип тестирования, который проводится над продуктом.

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

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

Внутри функционального тестирования проводится как позитивное, так и негативное тестирование. Что это значит? Позитивное тестирование — это выполнение тестов по требованиям продукта, не провоцируя каких-либо неверных действий в программе. В частности, позитивным сценарием к форме ввода данных может быть ввод валидных данных, т. е. допустимых значений (например, поле “номер телефона” нельзя заполнять ничем, кроме цифр). При позитивной проверке вводим в поле цифры.

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

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

Вообще есть два подхода к выполнению функционального тестирования: первый основан на требованиях (requirements-based), второй — на бизнес процессах (business-process-based).

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

При втором подходе тесты составляются на основе знаний бизнес-процессов или пользовательских и бизнес-историй. Такие тесты называются пользовательскими (use case). Они проектируются исходя из каждодневных операций пользователя в продукте или с помощью самого продукта. Для программы из примера выше пользовательский кейс — оформление кредита наличными или кредитной карты клиенту.

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

Нефункциональное тестирование (НФТ), также как и функциональное, проводится на всех уровнях. Его целью является проверка того, насколько качественно и как быстро работает продукт (например, как быстро загружается страница сайта). Обычно характеристики, которые тестируют, можно измерить по определённой шкале и сделать вывод о том, удовлетворяет ли работа продукта пользователей. Характеристиками нефункционального тестирования являются производительность, удобство использования, нагрузка, способность к восстановлению, надёжность, переносимость. Но этими пунктами НФТ не ограничивается. Вообще стандарт ISO выделил несколько характеристик для того, чтобы в индустрии повсеместно использовалась одна терминология. Мы приведем примеры нескольких.

  • Функциональное соответствие (Functional suitability) — это полнота и пригодность продукта, его корректность и уместность в принципе.
  • Эффективность производительности (Performance efficiency) определяет мощность продукта, его время отклика, использование ресурсов.
  • Удобство пользования (Usability) — его работоспособность, возможность обработки пользовательских ошибок, сам пользовательский интерфейс, с помощью которого пользователь может легко начать работать.
  • Безопасность (Security) — надёжность и сохранность данных, а также возможность продукта противостоять атакам извне.

Такой список нефункциональных характеристик мы рассмотрели для знакомства на этом уроке.

Мы разобрали два основных типа тестирования, цель которых — убедиться в правильности функционирования продукта и в способности нормально работать в различных условиях.

Третий тип тестирования — тестирование внутренней структуры системы или её архитектуры. На этом этапе нас интересует, как же работает продукт внутри. Это тестирование покажет, сколько элементов кода и структуры покрыто тестами. Для этого есть различные техники измерения покрытия, например процент покрытия строчек или веток кода. И выполняется оно чаще всего на компонентном или интеграционном уровне тестирования. На пользовательском приёмочном уровне оно не имеет смысла. При выполнении тестирования на интеграционном уровне за основу тестов можно брать архитектуру системы, иерархический вызов компонентов, чтобы проверить все условия вызова одного компонента и последующие вызовы от него. При системном тестировании базой может служить бизнес-модель продукта.

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

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

Тестирование изменений делится на два типа: повторное или подтверждающее и регрессионное тестирование.

Подтверждающее/повторное тестирование (confirmation testing / re-testing) необходимо для проверки ранее обнаруженного дефекта. Например, тестируя какую-то функциональность продукта, тестировщик обнаруживает дефект, заводит баг-репорт и отдает его на исправление разработчикам. Так вот, после того, как разработчик исправил дефект, он возвращается тестировщику на проверку. Тем самым тестировщик подтверждает, что дефект исправлен и продукт работает верно. Важно проводить повторное тестирование при тех же самых условиях и с такими же входящими данными, чтобы максимально точно воспроизвести ситуацию, когда дефект был обнаружен.

В итоге после повторного тестирования, когда тест проходит положительно, мы знаем только то, что дефект исправлен и что в этой части продукт работает верно. Достаточно ли этого? НЕТ. Исправление дефекта косвенно или прямо могло задеть другие функции продукта и поломать его в другом месте.

Чтобы убедиться в том, что в продукте не появятся неожиданные дефекты, существует регрессионное тестирование (regression testing).

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

Исходя из этого название “регрессионное” не совсем верно для такого типа тестирования. Правильнее было бы называть его “анти-регрессионное” тестирование, потому что мы проверяем продукт на то, не сдвинулся ли он в сторону регресса, всё ли работает как ранее, верно ли, не сломало ли исправление что-нибудь в существующей функциональности. Это касается не только исправлений дефектов. Сюда относятся любые изменения на любом уровне, будь то добавление новой функциональности или исправление существующей для внесения каких-нибудь дополнительных требований.

Для больших, долгих проектов команды разрабатывают определённый набор регрессионных тестов (иногда такой набор тестов называют просто “регрешн” от англ. regression). В этом наборе собраны проверки для всех важных и критичных функций продукта. Полезно иметь такой набор тестов для всех уровней тестирования, чтобы быстро проводить их каждый раз при новом выпуске компонента или системы в целом. Поэтому эти кейсы — первые кандидаты на автоматизацию, для того чтобы не тратить много времени на проверку их вручную.

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

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

Другая классификация типов тестирования

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

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

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

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

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

Дымовое тестирование — не единственное в этой классификации, здесь может быть так называемое Happy Path тестирование и Sanity-тестирование (Sanity Testing). К первому традиционно относят кейсы использования обычного пользователя, т. е. то, что в 70 процентах случаев выполняет в приложении пользователь (например, авторизация в блог, переход на домашнюю страницу, открытие поста в блоге и отметка “нравится”).

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

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

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

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

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