Оглавление
Часть 2: Связываем тест-кейсы с wiki-страницами
Использование проверочных скриптов
Проверочные скрипты, в частности, на SQL, значительно ускоряют выполнение проверок при тестировании методов (функций) бэкенда, которые изменяют данные. Автоматизация в данном случае обычно сводится к сравнению ожидаемого и фактического наборов данных (датасетов). Иногда без использования кода крайне тяжело качественно решить задачу. Например, на проекте, где я работаю, необходимо было сравнить результаты расчета разных версий оптимизатора в двух десятках таблиц. Записей было много. Ограничить используемый набор данных я не мог из-за сложной бизнес-логики с учетом отсутствия документации. Равно как и подготовить свои тестовые данные в разумный срок - по той же причине.
Для тестирования я использовал SQL-запрос, описанный ниже. Под каждую таблицу с отдельным набором данных в CTE expected (реальные наборы данных и названия таблиц с проекта заменены на те, что я подготовил для статьи):
with
expected as (
select *
from (
-- Описываем записи с ожидаемыми данными, включая названия столбцов после t в конце CTE
values
('16' , 'iPhone' ),
('Galaxy S23', 'Samsung'),
('14' , 'Xiaomi' ),
('P60' , 'Huawei' ),
('X6' , 'POCO' )
) t("name", manufacturer)
),
-- Ищем записи с фактическими данными по тем же столбцам, что заданы в предыдущем CTE
fact as (
select "name", manufacturer
from smartphones.models
),
-- Ищем несоответствия между ожидаемым и фактическим наборами записей
mismatches as (
select
*,
-- Добавляем столбец с текстовой строкой к записям для упрощения идентификации
' (Ожидаемая запись)' as status
from (
select *
from expected
except all
select *
from fact
) sq1
union all
select
*,
' (Фактическая запись)' as status
from (
select *
from fact
except all
select *
from expected
) sq2
order by manufacturer, status -- Добавляем сортировку несоответствий при необходимости
),
-- Собираем полученную информацию
info as (
select
(select count (*) from mismatches) as count_mismatches,
(select string_agg(
concat_ws(', ', manufacturer, "name") || status
, E'\n') from mismatches) as mismatches,
(select count (*) from expected) as count_expected,
(select count (*) from fact) as count_fact
)
select *
from info
Кстати, логика в этом запросе универсальна: его можно использовать для тестирования данных в любой таблице базы данных в СУБД типа SQL. Конечно, с предварительной заменой проверяемой таблицы, её столбцов и ожидаемого датасета. Если хотите попробовать применить такой запрос у себя на проекте - дайте знать, опишу, что нужно заменить.
Если датасеты идентичны, запрос показывает отсутствие несоответствий (mismatches):
Заменим, например, (16, 'iPhone') на (15, 'iPhone'). Теперь запрос показывает наличие несоответствий и дает по ним информацию:
Несоответствия также будут засчитаны при наличии лишних записей и/или при отсутствии ожидаемых:
Шаг, в котором есть ссылка на wiki-страницу с кодом, обычно содержит меньше текста, чем шаг, в котором нет такой ссылки Это связано с тем, что действия и их логика переносятся из текстового поля в скрипт. Тест-кейсы с такими шагами получаются менее многословными, что уменьшает количество времени на ознакомление с ними и на их выполнение. Это, в свою очередь, снижает влияние bus factor'а на проекте.
Сравним шаг для проверки соответствия фактических данных ожидаемым в БД (например, после выполнения какого-либо метода или алгоритма):
1-й вариант
Описание: Анализ данных в таблице models
Шаг:
Перейти по пути в DBeaver: _local.smartphones.models
Открыть раздел Data таблицы models
Сравнить записи в таблице с ожидаемыми:
name | manufacturer |
---|---|
16 | iPhone |
Galaxy S23 | Samsung |
14 | Xiaomi |
P60 | Huawei |
Ожидаемый результат: Набор фактических записей в таблице models соответствует ожидаемому
2-й вариант
Описание: Анализ данных в таблице model
Шаг: Убедиться, что фактический набор записей соответствует ожидаемому, выполнив SQL-запрос:
Ссылка на wiki-страницу с кодом
Ожидаемый результат: Значение в столбце count_mismatches: 0
Сумма слов в разделах Шаг и Ожидаемый результат:
В 1-м варианте: ~35 слов с учетом чисел в столбце name;
В 2-м варианте: 15 слов без учета словосочетания Ссылка на wiki-страницу с кодом - вместо него, соответственно, предполагается ссылка. БД, в которой необходимо выполнить запрос (smartphones), - указывается на wiki-странице.
Разница - двукратная. И это пример с 1-м шагом и 4-мя проверяемыми записями. На реальном проекте с сотнями тест-кейсов и тысячами проверяемых значений получается крайне существенная экономия ресурсов. Выполнение запроса также занимает меньше времени, чем выполнение 3-х действий, описанных в 1-м варианте.
Проверочные скрипты можно использовать и в тест-кейсах по фронтенду. Вернемся к импровизированному UI, который я показывал выше, и добавим туда, на страницу, несколько записей:
Здесь прежде всего важно проверить CRUD: просмотр и изменение данных, хранящихся в таблице models. Опишу, как в данном случае можно применить скрипты.
Для всех тест-кейсов:
Скрипт для очистки проверяемых данных, выполняемый в предусловии, - на случай, если данные остались с предыдущих прогонов. Это также важно для предотвращения потенциально возникающей ошибки, не связанной с кодом приложения - срабатывание ограничения уникальности ключа:
delete from smartphones.models where "name" in ('X6', '16', 'Galaxy S23', '14', 'P60') and manufacturer in ('POCO', 'iPhone', 'Samsung', 'Xiaomi', 'Huawei')
Скрипт для очистки проверяемых данных, выполняемый в постусловии - чистим за собой, чтобы не захламлять БД (такой же, как в предусловии).
Для каждого отдельного тест-кейса:
Создание:
Скрипт для проверки создания записей в таблице после их добавления на UI (описан выше, в начале раздела Использование проверочных скриптов в шагах);
Чтение:
Скрипт для добавления проверяемых данных, выполняемый в предусловии:
insert into smartphones.models (id, "name", manufacturer) values (1, '16', 'iPhone'), (2, 'Galaxy S23', 'Samsung'), (3, '14', 'Xiaomi'), (4, 'P60', 'Huawei'), (5, 'GT', 'realme'), (6, 'X6', 'POCO');
Обновление:
Скрипт для добавления проверяемых данных, выполняемый в предусловии (описан выше, в Чтении);
Скрипт для проверки изменения записей в таблице после их изменения на UI (запрос, аналогичный тому, что описан в начале раздела Использование проверочных скриптов в шагах, но с корректировкой одного из значения на изменяемое);
Удаление:
Скрипт для добавления проверяемых данных, выполняемый в предусловии (описан выше, в Чтении);
Скрипт для проверки отсутствия записей в таблице после их удаления на UI:
select exists (select * from smartphones.models)
Кстати, метаданные таблиц мы тоже проверяем автоматически:
Этот запрос также можно использовать с любой таблицей в СУБД типа SQL. Ему посвящена отдельная статья: https://habr.com/ru/articles/862562/
Использование артефактов тест-дизайна
Wiki-страницы пространства тестирования полезны не только для частичной автоматизации. Но и для структуризации большого количества тестовых данных. Бывают случаи, когда объект тестирования настолько комплексный, что даже после применения техник тест-дизайна все равно остается слишком много проверок. Я столкнулся с такой ситуацией при работе с алгоритмом, для которого предполагается передача 32-х входных параметров разных типов. Для одного из них валидными значениями считаются те, что относятся к определенному списку. Группа других - коэффициенты, для которых ожидаются значения в диапазоне от 0 до 1. Есть и группа строковых параметров, где каждый из них принимает лишь 1 валидное значение в текущей версии алгоритма. При этом 15 параметров - обязательны для заполнения перед каждым запуском, а остальные - опциональны.
После применения техник тест-дизайна получилось 111 проверок, необходимых для тестирования по поставленной задаче. 51 позитивная проверка и 60 - негативных. Позитивные проверки можно объединять и тестировать совместно в рамках 1-го шага, а вот каждую негативную проверку нужно выполнять отдельно. Это связано с тем, что в алгоритме единовременно возможно срабатывание только 1-го exception'а. После этого его выполнение завершается с ошибкой. С учетом подобных особенностей финальное количество наборов тестовых данных (ТД) составило 73. Мы их раскидали по 4-м тест-кейсам:
Все наборы ТД для позитивных проверок;
Все наборы ТД для негативных проверок;
Сокращенные наборы ТД для позитивных проверок;
Сокращенные наборы ТД для негативных проверок.
Тест-кейсы со всеми наборами мы с коллегой прогнали единожды, а сокращенные подготовили для регрессионного тестирования. При этом значения для сокращенных наборов подбираются с помощью отдельного SQL-скрипта случайным образом из пула всех тестовых значений. Такой подход позволяет, с одной стороны, экономить время, с другой, - снижает влияние парадокса пестицида.
Наименьшее количество шагов среди этих тест-кейсов - в том, который содержит сокращенные наборы ТД для позитивных проверок, - 6 шагов. 3 из них содержат наборы значений, которыми нужно заполнить параметры (остальные служат для проверки факта заполнения таблиц после прогона алгоритма). С учетом того, того, что 15 входных параметров являются обязательными для заполнения, каждый из шагов будет содержать примерно такой текст:
В селекте "Алгоритм" выбрать "Алгоритм 1"
Заполнить входные параметры:
param1:
1param2:
0param3:
0param4:
Yparam5:
simple-modeparam6:
12param7:
6param8:
calendarparam9:
history_valueparam10:
new_valueparam11:
history_tableparam12:
new_tableparam13:
value_tableparam14:
resource;area;supplierparam15:
true
Нажать кнопку "Запустить". Перейти на страницу "Анализ выполнения алгоритмов"
Вам о чем-то говорят названия параметров выше и значения для них? Если нет, то это ожидаемо. Думаю, это хорошая иллюстрация того, как тестировщики видят нетривиальные тест-кейсы, написанные коллегами. При выполнении описанных проверок по-хорошему понимать формулировки необязательно, если шаг остается воспроизводимым. Однако в случае доработок функциональности, за которыми ожидается апдейт тест-кейсов, понимание становится необходимым. И если тестировщик не является автором такого тест-кейса, ему приходится обращаться к документации и осваивать её в большей степени целиком, чтобы понять контекст.
Использование связанной wiki-страницы упрощает и действия в шаге, и понимание контекста за счет возможности размещения артефакта тест-дизайна, где описаны не только значения для тестирования, но и логика их формирования. Шаг с таким оформлением будет выглядеть так:
В селекте "Алгоритм" выбрать "Алгоритм 1"
Заполнить входные параметры, используя ТД из wiki-страницы:
Ссылка на страницуНажать кнопку "Запустить". Перейти на страницу "Анализ выполнения алгоритмов"
Примерно так и выглядит шаг из реального тест-кейса. На связанной wiki-странице у нас содержится та же информация, что во 2-м пункте, в предыдущем варианте. Здесь хранится 1 тестовый набор данных для каждого конкретного шага. С этой wiki-страницей связана другая, где хранятся сведения по логике формирования тестовых данных и таблица с описанием проверок. Покажу, как она выглядит для шага, описанного выше, но, для краткости, с информацией по 2-м параметрам вместо 15-ти:
Параметр | Значение | Тип | ТД |
---|---|---|---|
param1 | Не заполнен | Негативный | Ссылка на wiki-страницу 1 |
- 0.1 | Негативный | Ссылка на wiki-страницу 2 | |
0 | Позитивный | Ссылка на wiki-страницу 3 | |
0.3 | Позитивный | Ссылка на wiki-страницу 4 | |
1 | Позитивный | Ссылка на wiki-страницу 5 | |
1.1 | Негативный | Ссылка на wiki-страницу 6 | |
... | ... | ... | ... |
param4 | Не заполнен | Негативный | Ссылка на wiki-страницу 7 |
D | Позитивный | Ссылка на wiki-страницу 8 | |
W | Позитивный | Ссылка на wiki-страницу 9 | |
M | Позитивный | Ссылка на wiki-страницу 10 | |
Q | Позитивный | Ссылка на wiki-страницу 11 | |
Y | Позитивный | Ссылка на wiki-страницу 12 | |
K | Негативный | Ссылка на wiki-страницу 13 | |
... | ... | ... | ... |
Схема связей артефактов тестирования в этом случае выглядит так:
Здесь прямоугольники - это wiki-страницы, овалы - разделы wiki-страницы, пятиугольник - тест-кейс. На странице "Общие данные для строковых параметров с 1-м валидным значением" содержатся значения для параметров param8 - param15. Это те, что:
Есть и группа строковых параметров, где каждый из них принимает лишь 1 валидное значение в текущей версии алгоритма.
В Confluence мы подтягиваем информацию для такой страницы с помощью макросов Excerpt и Excerpt Include. Получается наследование данных, которое также упрощает поддерживаемость.
Выглядит это примерно так (значения параметров param8 - param15 наследуются, остальные - отличаются):
Wiki-страница с тестовыми данными 1
Общие данные для строковых параметров с 1-м валидным значением
param8:
calendarparam9:
history_valueparam10:
new_valueparam11:
history_tableparam12:
new_tableparam13:
value_tableparam14:
resource;area;supplierparam15:
true
param1:
1param2:
0param3:
0param4:
Yparam5:
simple-modeparam6:
12param7:
6
Wiki-страница с тестовыми данными 2
Общие данные для строковых параметров с 1-м валидным значением
param8:
calendarparam9:
history_valueparam10:
new_valueparam11:
history_tableparam12:
new_tableparam13:
value_tableparam14:
resource;area;supplierparam15:
true
param1:
0.5param2:
0.5param3:
0.5param4:
Mparam5:
advanced-modeparam6:
24param7:
12
Кстати, диаграмма иллюстрирует тезис, который я описал в разделе Суть подхода:
Это позволяет структурировать информацию атомарно: каждая страница содержит ровно столько сведений, сколько того требует контекст. Если нужно больше - можно переходить по указанным ссылкам.
Итак, связывание wiki-страниц с шагами тест-кейса позволяет:
Сократить количество текста в шагах тест-кейсов;
Переиспользовать повторяющиеся данные;
Добавить справочную информацию по тестируемой части функциональности, что упрощает понимание документации с точки зрения контекста тестирования. Мы также можем указать, что мы НЕ тестируем и по какой причине;
Сделать объекты более атомарными за счет распределения информации между тест-кейсом, wiki-страницей с артефактом тест-дизайна и wiki-страницей с тестовыми данными. Эти объекты связываются линками. Такой подход упрощает работу с информацией;
Внедрить частичную автоматизацию. В частности, через генерацию сокращенного набора данных из пула всех данных.
Данные запросов и сообщений для интеграционного тестирования
Несмотря на то, что в json'е по названию ключей и значениям для них можно понять бизнес-смысл запроса, - эта информация все еще содержит мало сведений о контексте тестирования. Почему это может быть важно? Предположим, по шагу тест-кейса необходимо отправить такой http-запрос:
Запрос
POST https://smartphones.ru/catalog
{
"smartphones": [
{
"brand": "TechOne",
"model": "X100 Pro",
"release_date": "2024-03-15",
"specs": {
"display": "6.7 inch OLED",
"processor": "Octa-core 3.2GHz",
"ram": "12GB",
"storage": "256GB",
"camera": {
"rear": "108MP + 12MP + 5MP",
"front": "32MP"
},
"battery": "4500mAh",
"os": "Android 14"
},
"price": 899,
"colors": ["Black", "Silver", "Aurora Blue"]
},
{
"brand": "Photonix",
"model": "PX-Magic",
"release_date": "2024-05-22",
"specs": {
"display": "6.5 inch AMOLED",
"processor": "Hexa-core 2.9GHz",
"ram": "8GB",
"storage": "128GB",
"camera": {
"rear": "64MP + 16MP",
"front": "20MP"
},
"battery": "4200mAh",
"os": "Photonix OS 3.0"
},
"price": 649,
"colors": ["Midnight Black", "Sunrise Gold", "Ocean Green"]
},
{
"brand": "NebulaTech",
"model": "Nebula Z2",
"release_date": "2024-02-10",
"specs": {
"display": "6.8 inch Super Retina",
"processor": "Quad-core 3.5GHz",
"ram": "16GB",
"storage": "512GB",
"camera": {
"rear": "200MP + 50MP + 12MP",
"front": "40MP"
},
"battery": "5000mAh",
"os": "Nebula OS 5.0"
},
"price": 1099,
"colors": ["Space Gray", "Starlight White", "Cosmic Red"]
},
{
"brand": "GalaxyPrime",
"model": "Galaxy Ultra S",
"release_date": "2024-06-30",
"specs": {
"display": "7.1 inch Dynamic AMOLED",
"processor": "Deca-core 3.8GHz",
"ram": "12GB",
"storage": "256GB",
"camera": {
"rear": "150MP + 24MP + 10MP",
"front": "32MP"
},
"battery": "4800mAh",
"os": "GPrime OS 4.0"
},
"price": 999,
"colors": ["Jet Black", "Pearl White", "Emerald Green"]
},
{
"brand": "Solarion",
"model": "Helios Max",
"release_date": "2024-07-15",
"specs": {
"display": "6.9 inch Fluid AMOLED",
"processor": "Octa-core 3.1GHz",
"ram": "8GB",
"storage": "128GB",
"camera": {
"rear": "100MP + 12MP + 8MP",
"front": "24MP"
},
"battery": "4700mAh",
"os": "Solarion UI 6.0"
},
"price": 749,
"colors": ["Solar Black", "Sunburst Orange", "Twilight Blue"]
},
{
"brand": "HyperNova",
"model": "NovaX Edge",
"release_date": "2024-08-25",
"specs": {
"display": "6.4 inch OLED",
"processor": "Hexa-core 2.8GHz",
"ram": "6GB",
"storage": "64GB",
"camera": {
"rear": "48MP + 12MP",
"front": "16MP"
},
"battery": "4100mAh",
"os": "Hyper OS 2.0"
},
"price": 499,
"colors": ["Graphite Gray", "Lunar Silver", "Coral Pink"]
}
]
}
Этот json мне сгенерировала нейросеть. Понятия не имею, что там за данные. Тем не менее он вполне сгодится в качестве иллюстрации. На тест может прийти задача, по которой необходимо отправлять запросы с телом подобного размера. Составить проверки для такого случая будет проще после применения техник тест-дизайна. Однако, как правило, результатом подготовки тестовых артефактов по задаче становится именно набор с тест-кейсами. Артефакты тест-дизайна при таком подходе считаются полезными, но необязательными. Они могут храниться локально или в wiki, но без каких-либо связей.
Тем временем привязанная к тест-кейсам таблица с описанием проверок дает значительно больше контекста по объекту тестирования. Это упрощает работу, что, в свою очередь, приводит к экономии ресурсов. Автору тест-кейса, который составил описанный выше http-запрос, вполне может через некоторое время прийти задача на расширение тестового набора в связи, например, с доработками по связанной функциональности. И если в рамках этой активности необходимо будет править тело запроса, то без артефакта тест-дизайна сделать это будет затруднительно. Особенно по прошествии времени, после переключения на другие задачи. В конце концов с объемными json'ами зачастую неудобно работать в TMS. Например, в Xray, в поле шага, отображается только часть текста и скролл.
Рассмотрим еще пример. В рамках тест-кейса с позитивными проверками необходимо отправить 2 http-запроса:
{
"smartphones": [
{
"manufacturer": "Nothing",
"model": "Nothing Phone 2",
"price": 65000
}
]
}
и
{
"smartphones": [
{
"manufacturer": "Huawei",
"model": "P60",
"price": 55000,
"country": "China"
},
{
"manufacturer": "Xiaomi",
"model": "Xiaomi Redmi Note 11 Pro 4G",
"price": 25000
}
]
}
Выглядит значительно проще. Тем не менее непонятно, почему в теле 1-го запроса - 1 объект в массиве smartphones, а во 2-м - 2 объекта. И почему во 2-м запросе, в 1-м объекте массива - 4 параметра, а во 2-м - 3. И должно ли все быть именно так. И как эти данные соотносятся с негативными проверками.
Несмотря на то, что эти тела запросов - небольшие, даже они могут заставить задуматься автора тест-кейса, оптимально ли составлены проверки. Не говоря уже о коллегах. Дополнительную информацию в этом случае можно извлечь из таблицы с проверками, которая хранится на привязанной к тест-кейсу wiki-странице:
Номер проверки | Значения | Тип | Проверка | Датасет |
---|---|---|---|---|
Количество объектов в массиве | ||||
1 | 0 | Негативный | Сообщение об ошибке отсутствия объектов в массиве | Ссылка на страницу с датасетом 1 |
2 | 1 | Позитивный | 1 объект в массиве | Ссылка на страницу с датасетом 2 |
3 | 2 | Позитивный | Максимальное количество объектов согласно требованию + несколько объектов в массиве | Ссылка на страницу с датасетом 3 |
4 | 3 | Негативный | Возврат сообщения о превышении количества объектов в массиве | Ссылка на страницу с датасетом 4 |
Количество строк в объекте массива | ||||
5 | 0 | Негативный | Сообщение об ошибке отсутствия обязательных ключей Связь с проверками 11 и 12 | Ссылка на страницу с датасетом 5 |
6 | 1 | Негативный | Сообщение об ошибке отсутствия обязательных ключей Связь с проверками 11 и 12 | Ссылка на страницу с датасетом 6 |
7 | 2 | Негативный | Сообщение об ошибке недостаточного количества строк в объекте | Ссылка на страницу с датасетом 7 |
8 | 3 | Позитивный | Минимальное количество строк в объекте согласно требованию Связь с проверкой 16 | Ссылка на страницу с датасетом 2 |
9 | 5 | Позитивный | Максимальное количество строк в объекте согласно требованию | Ссылка на страницу с датасетом 3 |
10 | 6 | Негативный | Переход верхней границы количества строк в объекте | Ссылка на страницу с датасетом 9 |
Ключи в объекте массива | ||||
11 | Нет model, но есть manufacturer | Негативный | Сообщение об ошибке отсутствия обязательного ключа model | Ссылка на страницу с датасетом 10 |
12 | Нет manufacturer, но есть model | Негативный | Сообщение об ошибке отсутствия обязательного ключа manufacturer | Ссылка на страницу с датасетом 11 |
13 | Есть и manufacturer, и model, но нет других ключей | Негативный | Сообщение об ошибке недостаточного количества строк в объекте Связь с проверкой 7 | Ссылка на страницу с датасетом 12 |
14 | 2 ключа manufacturer и 1 ключ model | Негативный | Сообщение об ошибке наличия 1-й пары дублей Дублируется обязательный ключ | Ссылка на страницу с датасетом 13 |
15 | 2 ключа model и 1 ключ manufacturer | - | Избыточно. Кейс покрывается проверками 14 (строка-дубль) и 17 (есть 3 строки, включая 2 обязательные) | - |
16 | 2 ключа model и продублированный необязательный ключ | Негативный | Сообщение об ошибке наличия 2-х пар дублей в случае, когда дубль ключа - один Дублируется и обязательный, и необязательный ключи | Ссылка на страницу с датасетом 14 |
17 | 3 ключа model и необязательный ключ с 2-мя дублями | Негативный | Сообщение об ошибке наличия 2-х пар дублей в случае, когда дублей ключа - несколько | Ссылка на страницу с датасетом 15 |
18 | 1 ключ manufacturer, и 1 ключ model, и еще 1 ключ | Позитивный | Минимальное удовлетворение требований Связь с проверкой 8 | Ссылка на страницу с датасетом 2 |
19 | 1 ключ manufacturer, и 1 ключ model, и еще 3 ключа | Позитивный | Связь с проверкой 9 | Ссылка на страницу с датасетом 3 |
Значения ключа manufacturer | ||||
20 | ... | ... | ... | ... |
Я описал здесь не все, что можно посмотреть в json'ах. Тем не менее думаю, это подходящая иллюстрация того, что за небольшими тестовыми данными может скрываться объемный контекст тестирования. Артефакты тест-дизайна, связываемые с тест-кейсами, упрощают работу с задачами по тестированию, особенно с теми, где используется большое количество тестовых данных.
Wiki-страницы полезны и при работе с сообщениями MQ. Все, что описано для http-запросов, применимо и к таким сообщениям. Также в случае с MQ поддерживаемость можно упростить через наследование страницы с хэдерами на страницах с телом сообщения:
Продолжение - в следующей части.