С одной стороны, Confluence приведен в качестве примера:
Описываемый подход довольно гибок, поэтому его вполне можно внедрить и при использовании других инструментов.
Можно использовать и другие Wiki-системы для расширения системы тестовой документации.
С другой стороны, да, если произойдет миграция только документов из Wiki-системы, какой бы она ни была, но не тест-кейсов из TMS, то нужно будет переделывать все связи. Гипотетически, этого можно избежать, если налаживать связи исключительно внутри TMS, но, мне кажется, едва ли найдется такой инструмент, где можно будет удобно и без костылей это сделать. Так или иначе, хочется иметь возможность распределять информацию по разным объектам (артефактам), чтобы переиспользовать их.
в Аллюр
Имеете в виду, Allure Testops? Если да, не работал с ним, к сожалению. Можно, получается, прямо в интерфейсе отобразить, например, содержимое файла .sql и скопировать его?
В комменте выше описал, почему в моем случае этот запрос оказался полезен. Возможно, эта информация подойдет в качестве ответа.
А где гарантия, что если вы забыли колонку при создании таблицы, вы не забудете её в тестах
Создает один специалист, тесты пишет другой. Так снижается вероятность ошибки. 100%-х гарантий тестирование дать не может, полагаю, исходя из принципов тестирования.
Это правда, DDL написать проще. В моем случае удобство использования такого запроса связано с тем, что:
Требования к таблицам описываются аналитиком в виде отдельной таблицы. DDL при этом отсутствует;
Новые таблицы, описанные в требованиях, добавляются по согласованию либо аналитиком, либо разработчиком;
Бывает так, что добавленные новые таблицы правят в БД, но вместе с тем не обновляют таблицу с требованиями в Wiki. Отсюда получается расхождение;
Со своей стороны я, будучи QA, не завожу новые таблицы, но могу проверить их на соответствие требованиям. Это можно сделать глазами, открыв два окна и сравнивая значения, либо через описанный запрос. С учетом того, что после внесения обновлений в требования могут быть опять, к примеру, изменения в БД, то в этом случае прогнать запрос значительно быстрее, чем проводить сравнение глазами. У нас еще и алгоритмы, являющиеся частью функциональностей проекта, могут падать из-за измененных метаданных таблиц. И проверочные запросы помогают локализовать дефект, когда в логах появляется название одной из таких таблиц
И я же правильно понял, что в проверке внешних ключей вы не достаёте информацию о том, в какую таблицу ведёт этот внешний ключ? Тогда запрос будет показывать неверные результаты.
Ага, есть такое. У меня по задачам не возникало такой ситуации, поэтому не думал об этом. Весомое и дельное замечание. Название таблиц, на которое направлено ограничение, а также схемы, в которых хранятся эти таблицы, можно вытащить по запросу в CTE constraints_query. Однако этого недостаточно для корректной работы запроса, нужно также доработать логику сравнения ограничений. Оставлю здесь исправленный вариант для описанного в статье примера:
Исправленный код
with
-- Настраиваем запрос: задаем название таблицы и название схемы
params(table_name, table_schema) as (
values (
'price',
'public'
)
),
-- Описываем ожидаемые столбцы таблицы
required_columns(column_name, data_type, is_nullable, character_maximum_length) as (
values
('id', 'bigint', 'NO', null),
('price_value', 'numeric', 'NO', 18.6),
('model_id', 'integer', 'NO', null),
('comment', 'character varying', 'YES', 255)
),
-- Описываем ожидаемые ограничения
required_constraints(column_name, constraint_type, constraint_table, table_schema) as (
values
('id', 'PRIMARY KEY', 'price', 'public'),
('id', 'FOREIGN KEY', 'models', 'public'),
('model_id', 'FOREIGN KEY', 'price', 'public'),
('price_value', 'UNIQUE', 'price', 'public'),
('model_id', 'UNIQUE', 'price', 'public')
),
-- Находим информацию о столбцах тестируемой таблицы и добавляем обработку null'ов
columns_info as (
select
column_name, data_type, is_nullable,
coalesce(numeric_precision, 0) as numeric_precision,
coalesce(numeric_scale, 0) as numeric_scale,
coalesce(character_maximum_length, 0) as character_maximum_length
from information_schema.columns
where
table_name = (select table_name from params)
and table_schema = (select table_schema from params)
),
-- Проверяем существование таблицы и подсчитываем количество столбцов в ней
check_table_exist as (
select
case when count_all_fields < 1 then false else true end table_exists,
case when count_all_fields < 1 then 1 else 0 end table_exists_error,
count_all_fields
from (
select count (*) as count_all_fields
from columns_info
) sq
),
-- Сравниваем ожидаемый и текущий наборы атрибутов таблицы
fields_comparison as (
select t.*
from columns_info t
inner join required_columns r
on t.column_name = r.column_name
and t.data_type = r.data_type
and t.is_nullable = r.is_nullable
and (
-- Сравниваем целую часть десятичных значений
case
when t.data_type = 'numeric'
then t.numeric_precision = trunc(r.character_maximum_length::numeric)
end
and
-- Сравниваем дробную часть десятичных значений
case
when t.data_type = 'numeric'
then t.numeric_scale = (r.character_maximum_length::numeric - trunc(r.character_maximum_length::numeric))
* power(10, length(split_part(r.character_maximum_length::text, '.', 2)))
end
or t.character_maximum_length = coalesce(r.character_maximum_length::numeric, 0)
)
),
-- Ищем лишние столбцы и считаем их количество
check_unexpected_fields as (
select
count (column_name) as count_unexpected_fields,
string_agg(column_name, ', ') as unexpected_fields
from (
select column_name
from columns_info
except
select column_name
from required_columns
) sq
),
-- Ищем недостающие столбцы и считаем их количество
check_missing_fields as (
select
count (column_name) as count_missing_fields,
string_agg(column_name, ', ') as missing_fields
from (
select column_name
from required_columns
except
select column_name
from columns_info
) sq
),
-- Ищем невалидные столбцы и считаем их количество
check_invalid_fields as (
select
count (column_name) as count_invalid_fields,
string_agg(column_name, ', ') as invalid_fields
from (
select column_name
from required_columns
except
select column_name
from fields_comparison
except
select string_to_table(missing_fields, ', ')
from check_missing_fields
) sq
),
-- Ищем все ограничения для таблицы
constraints_query as(
select
t1.constraint_type,
t1.table_name as foreign_table_name,
t1.table_schema as foreign_table_schema,
t2.table_schema,
t2.table_name,
t2.column_name,
t3.column_name as foreign_column
from information_schema.table_constraints t1
left join information_schema.constraint_column_usage t2
on t1.constraint_name = t2.constraint_name
left join information_schema.key_column_usage as t3
on t1.constraint_name = t3.constraint_name
and t1.table_schema = t3.table_schema
where
t1.table_name = (select table_name from params)
and t1.constraint_schema = (select table_schema from params)
and t2.column_name is not null
),
-- Включаем значения зависимых ключей (foreign_column) в список ограничений (column_name)
union_foreign_ref_columns as (
select distinct on (constraint_type, column_name) column_name, constraint_type, table_name, table_schema
from constraints_query
union all
select foreign_column, constraint_type, foreign_table_name, foreign_table_schema
from constraints_query
where constraint_type = 'FOREIGN KEY'
),
-- Ищем лишние ограничения и считаем их количество
check_unexpected_constraints as (
select
count (column_name) as count_unexpected_constraints,
string_agg(concat_ws(', ', column_name, constraint_type, table_name, table_schema), E'\n') as unexpected_constraints
from (
select *
from union_foreign_ref_columns
except
select column_name, constraint_type, constraint_table, table_schema
from required_constraints
) sq
),
-- Ищем недостающие ограничения и считаем их количество
check_missing_constraints as (
select
count (column_name) as count_missing_constraints,
string_agg(concat_ws(', ', column_name, constraint_type, constraint_table, table_schema), E'\n') as missing_constraints
from (
select column_name, constraint_type, constraint_table, table_schema
from required_constraints
except
select *
from union_foreign_ref_columns
) sq
),
-- Собираем полученные данные
checks as (
select
-- Выводим всю информацию об ошибках и суммируем их количество
table_exists_error + count_unexpected_fields + count_missing_fields + count_invalid_fields +
count_unexpected_constraints + count_missing_constraints as errors,
*
from check_table_exist
cross join check_unexpected_fields
cross join check_missing_fields
cross join check_invalid_fields
cross join check_unexpected_constraints
cross join check_missing_constraints
)
select *
from checks
С одной стороны, Confluence приведен в качестве примера:
Можно использовать и другие Wiki-системы для расширения системы тестовой документации.
С другой стороны, да, если произойдет миграция только документов из Wiki-системы, какой бы она ни была, но не тест-кейсов из TMS, то нужно будет переделывать все связи. Гипотетически, этого можно избежать, если налаживать связи исключительно внутри TMS, но, мне кажется, едва ли найдется такой инструмент, где можно будет удобно и без костылей это сделать. Так или иначе, хочется иметь возможность распределять информацию по разным объектам (артефактам), чтобы переиспользовать их.
Имеете в виду, Allure Testops? Если да, не работал с ним, к сожалению. Можно, получается, прямо в интерфейсе отобразить, например, содержимое файла .sql и скопировать его?
Ни в коем случае не воспринял ваш комментарий как негативный. В ответе попытался добавить контекста, которого, возможно, не хватило в статье.
Спасибо вам! :)
А вы как проверяете?)
В комменте выше описал, почему в моем случае этот запрос оказался полезен. Возможно, эта информация подойдет в качестве ответа.
Создает один специалист, тесты пишет другой. Так снижается вероятность ошибки. 100%-х гарантий тестирование дать не может, полагаю, исходя из принципов тестирования.
Это правда, DDL написать проще. В моем случае удобство использования такого запроса связано с тем, что:
Требования к таблицам описываются аналитиком в виде отдельной таблицы. DDL при этом отсутствует;
Новые таблицы, описанные в требованиях, добавляются по согласованию либо аналитиком, либо разработчиком;
Бывает так, что добавленные новые таблицы правят в БД, но вместе с тем не обновляют таблицу с требованиями в Wiki. Отсюда получается расхождение;
Со своей стороны я, будучи QA, не завожу новые таблицы, но могу проверить их на соответствие требованиям. Это можно сделать глазами, открыв два окна и сравнивая значения, либо через описанный запрос. С учетом того, что после внесения обновлений в требования могут быть опять, к примеру, изменения в БД, то в этом случае прогнать запрос значительно быстрее, чем проводить сравнение глазами. У нас еще и алгоритмы, являющиеся частью функциональностей проекта, могут падать из-за измененных метаданных таблиц. И проверочные запросы помогают локализовать дефект, когда в логах появляется название одной из таких таблиц
Так?
Ага, есть такое. У меня по задачам не возникало такой ситуации, поэтому не думал об этом. Весомое и дельное замечание. Название таблиц, на которое направлено ограничение, а также схемы, в которых хранятся эти таблицы, можно вытащить по запросу в CTE constraints_query. Однако этого недостаточно для корректной работы запроса, нужно также доработать логику сравнения ограничений. Оставлю здесь исправленный вариант для описанного в статье примера:
Исправленный код
Спасибо вам!
Случаем, не осталось примера под рукой? Интересно было бы посмотреть)