Функциональное тестирование

1. Определение

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

Функциональные требования включают в себя:

1.      Функциональная пригодность (англ. suitability).

2.      Точность (англ. accuracy).

3.      Способность к взаимодействию (англ. interoperability).

4.      Соответствие стандартам и правилам (англ. compliance).

5.      Защищённость (англ. security).

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

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

Преимущества функционального тестирования:

Недостатки функционального тестирования:

3. Методы отбора тестов для тестирования черного ящика

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

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

3.1. Тестирование сценариев использования (use-cases)

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

Чтобы уменьшить число тестов, можно проверить только те переходы, которые имеют смысл для пользователя. Use-case - это логически завершенная последовательность действий. Например, открытие файла в Notepad - это use-case, а выбор пункта меню "Открыть файл" в Notepad - это не use-case, а лишь первый шаг юз-кейса "открытие файла".

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

3.2. Тестирование классов эквивалентности.

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

 Например, пусть мы тестируем программу для отдела кадров, в ней есть поле "Возраст соискателя".

Требования по возрасту у нас будут такие:

0-13 лет - не нанимать

14-17 лет - можно нанимать на неполный день

18-54 года - можно нанимать на полный день

55-99 лет - не нанимать

 

Чтобы проверить все возможные разрешенные данные нам нужно протестировать ввод чисел от 0 до 99. (Возможен ведь еще ввод отрицательных чисел и нечисловых данных.) Так ли необходимо тестировать все числа от 0 до 99? Программы построены на работе с диапазонами значений:

 

  if (age >= 0 && age <=13)

            hireStatus="NO";

  if (age >= 14 && age <=17)

            hireStatus="PART";

  if (age >= 18 && age <=54)

            hireStatus="FULL";

  if (age >= 55 && age <=99)

            hireStatus="NO";

 

Становится очевидным, что можно протестировать одно из чисел каждого диапазона. Например: 5, 15, 20, 60. А также граничные значения: 0, 13, 14, 17, 18, 54, 55, 99.

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

а) разбиение множества всех значений входной переменной на подмножества (классы эквивалентности), а затем

б) тестирование одного любого значения из каждого класса.

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

В данном случае имеем 12 классов эквивалентности (каждое из 8 граничных значений по сути является отдельным классом).

Чтобы проверить правильность работы программы на всех разрешенных данных, нужно провести 12 тестов.

Запрещенные данные тестируются аналогично - можно выделить классы эквивалентности "дробное число от 0 до 99", "отрицательное число", "число больше 99", "набор букв", "пустая строка" и т.д.

Таким образом, метод классов эквивалентности можно разделить на три этапа:

1. Тестирование разрешенных значений

2. Тестирование граничных значений

3. Тестирование запрещенных значений

Часто в литературе второй и третий этапы называют отдельными методами, но сути это не меняет.

3.3. Попарное тестирование.

Метод классов эквивалентности применяется для тестирования каждого входного параметра по отдельности.

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

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

Таким образом, можно упростить себе задачу и протестировать все возможные значения для каждой из пар параметров. Такой подход называется попарным тестированием (pairwise testing).

Вот пример. Пусть имеется 3 двоичных входных параметра (3 чекбокса). Количество всех возможных комбинаций - 2 в степени 3 = 8 , значит, нужно произвести 8 тестов. Давайте попробуем сэкономить, тестируя чекбоксы попарно.

Выпишем все комбинации для первого и второго чекбоксов:

 

 1-й 2-й

 0   0   

 0   1

 1   0

 1   1

 

Добавим третий столбец так, чтобы во втором и третьем столбце получились все 4 двоичные комбинации. Это можно сделать разными способами, мы сделаем так (на первый столбец можно не обращать внимания):

 

 1-й 2-й 3-й

 0   0   0  

 0   1   0

 1   0   1

 1   1   1

 

 

Итак, с помощью четырех наборов входных данных (четырех тестов) мы протестируем две пары параметров: первый со вторым и второй с третьим. Осталось протестировать пару "первый с третьим".

Выпишем отдельно 1 и 3 столбцы:

 

 1-й     3-й

 0       0  

 0       0

 1       1

 1       1

 

Как видно, мы имеем здесь две из четырех возможных комбинаций. Комбинации "01" и "10" здесь отсутствуют, а комбинации "00" и "11" присутствуют два раза. Ну что же, добавим еще 2 строки (еще два теста)

 

 1-й     3-й

 0       0  

 0       0

 1       1

 1       1

 0       1

 1       0

 

Вернем второй столбец на его законное место:

 

 1-й 2-й 3-й

 0   0   0   

 0   1   0

 1   0   1

 1   1   1

 0       1

 1       0

 

Выходит, что последние два теста можно проходить при любых значениях второго параметра. Можно дописать для определенности нули в эти пустые места:

 

 1-й 2-й 3-й

 0   0   0   

 0   1   0

 1   0   1

 1   1   1

 0   0   1

 1   0   0

 

Получаем 6 тестов вместо 8 при полном переборе.

Можно ли сэкономить еще? Оказывается, можно.

Вернемся к 1 шагу:

 1-й 2-й

 0   0   

 0   1

 1   0

 1   1

 

Давайте допишем третий столбец другим способом, поменяв порядок комбинаций:

 

 1-й 2-й 3-й

 0   0   1  

 0   1   0

 1   0   0

 1   1   1

 

Все комбинации для 1 и 2, а также для 2 и 3 параметра здесь есть. Отлично.

Посмотрим теперь на комбинации 1 и 3 параметра

 1-й     3-й

 0       1

 0       0

 1       0

 1       1

 

Ого! Что мы видим? Изменив порядок значений в третьем столбце, мы одним махом убили двух зайцев: скомбинировали и 2-й с 3-м, и 1-й с 3-м параметры.

Итого имеем всего 4 строки, то есть 4 теста, эквивалентные первоначальным шести:

 1-й 2-й 3-й

 0   0   1

 0   1   0

 1   0   0

 1   1   1

 

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

Вот строгое определение ортогонального массива:

Ортогональный массив OA(N,k,s,t) - это двумерный массив из N рядов (итераций) и k колонок (факторов) из набора S (т.е. факторы могут принимать любое из s значений), обладающий свойством:

выбрав любые t колонок (0<=t<=k) мы получим в рядах все комбинации сочетаний из s по t (Количество повторений одинаковых комбинаций обозначают через λ. Чаще всего рассматривают массивы, где λ = 1, т.е. каждая комбинация встречается только один раз). Параметр t называют мощностью ортогонального массива.

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

4. Сбор информации о программе при тестировании

4.1. Информация о базе данных

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

Например, если вводимая фамилия пользователя  записывается в поле типа "строка" длиной 128 символов, мы должны:

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

2) вне зависимости от того, существуют или нет такие фамилии, попробовать ввести строку длиннее 128 символов - программа не должна ломаться (должно показываться внятное сообщение об ошибке)

4.2. Информация о других внешних системах

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

4.3. Информация о коде программы

Если мы имеем доступ к коду программы, мы можем

а) увидеть специальные случаи, которые не попали в документ с требованиями и которые необходимо протестировать или, напротив

б) увидеть, что какие-то вещи тестировать не имеет смысла.

Как протестировать неизвестную программу или наращиваемый подход к первичному функциональному тестированию ПО.

 

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

 

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

 

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

 

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

 

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

 

1. Приемочное тестирование требований к ПО

3. Тестирование базовых сценариев

4. Анализ тенденций

5. Поэлементное тестирование входных данных (тестирование каждого элемента данных в отдельности на всех разрешенных классах эквивалентности) 

6. Комбинирование входных данных (тестирование комбинаций разрешенных значений для нескольких элементов данных)

7. Тестирование граничных значений

8. Тестирование ошибочных данных

 

1. Приемочное тестирование требований

Приемочное тестирование - это минимально необходимое. Можно придумать множество требований к требования. С точки зрения автора, QA должно обращать внимание в первую очередь на

1. наличие

2. непротиворечивость

3. проверяемость

4. полноту системы операций (create, read, update, delete).

 

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

Другие требования должны проверяться другими людьми.

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

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

3. Тестирование базовых сценариев

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

Кроме того, мы должны протестировать операции CRUD над всеми объектами программы (или ее части, если мы тестируем новую функциональность для существующего ПО), с типичными или дефолтными настройками.

5. Поэлементное тестирование входных данных

5.1. Определить элементы входных данных (все поля ввода)

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

5.3. Протестировать программу для каждого элемента в отдельности, в каждом из разрешенных состояний. 

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

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

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

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

6. Комбинирование входных данных.

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

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

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

7. Тестирование граничных значений.

Для каждой границы каждого элемента данных нужно протестировать 2 значения

Можно выделить 2 границы:

7.1. Границы диапазона данных

7.2. Границы размера поля (длина строки)

8. Тестирование невалидных данных (не имеющих смысла)

8.1. Пустая строка

8.2. Неверные числовые данные (напр., отрицательные или дробные, там где это не имеет смысла)

8.3. Недопустимый формат (например, для даты или телефона)

8.4. Недопустимые печатные символы (служебные или национальные символы там, где это не имеет смысла)

8.5. Недопустимые непечатные символы (перевод строки или табуляция там, где это не имеет смысла)

 

Пример

В качестве примера рассмотрим калькулятор с возможностью основных операций.

3. Базовые сценарии (операции CRUD)

В данном случае имеется возможность создание новой записи, редактирование и удаление. Получение информации не возможно.

4. Поэлементное тестирование входных данных

4.1. Имеется поле ввода

4.2. Классы эквивалентности для валидных значений:

 

Операторы:

1.      Сложение

2.      Вычитание

3.      Умножение

4.      Деление

 

Операнды:

1.      Простые

2.      С плавающей запятой

 

Всего получается 6 классов эквивалентности.

В дополнение имеется возможность очищать форму.

Так как от перемены зависимость от типа операнда не имеет значения, то в одном тесте можно проверять сразу ввод первого и второго типа операнда.

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

Итого получается 5 тестов.

 

6. Комбинирование входных данных.

Ввод простого числа и числа с плавающей запятой.

 

7. Тестирование граничных значений

Получение значения выходящего за рамки максимального числа, которое можно ввести в поле (максимум 10 символов, умножаем число состоящее из 10 символов на 10)

 

8. Тестирование невалидных данных

8.1 Ввод второй плавающей запятой

8.2 Деление на ноль

8.3 Вставка недопустимых символов из буфера обмена

8.4 действие с пустым полем.