Когда я был молодым…
Будучи молодым QA-инженером, я тестировал модуль регистрации пользователя одного десктопного приложения. Набирать сочетания случайных букв в качестве тестовых имени и фамилии мне изначально не нравилось, а использование личных данных считалось некорректным, поэтому я начал использовать имена и фамилии существующих политиков.
Барак Обама, Джордж Буш и другие регистрировались в сервисе кастинга голосовых актеров, проводили сессии голосовых записей, исправно платили по счетам и выполняли прочие бизнес-действия. Через некоторое время об этом узнал наш ПМ, который попросил отказаться от практики привлечения политиков в ряды наших тестовых пользователей, т.к. правила компании предполагают использование нейтральных тестовых данных. Передо мной встал выбор: пользоваться случайным набором букв или придумать некий шаблон, который я буду использовать в своих кейсах. Я выбрал второй вариант.
С тех пор персонаж Прошу прощения у всех реальных Василиев Пупкиных, если эта статья как-то вас обидит. Имя моего тестового персонажа всего лишь случайный выбор. </p>" data-abbr="Василий Пупкин ">Василий Пупкин стал моим лучшим другом. Теперь почти всегда, когда при тестировании нужно было вводить данные персоны, на помощь мне приходил Василий. Предположу, что у вас также есть шаблон, которым вы пользуетесь при быстром тестировании. Это своего рода Alter Ego тестеровщика.
Эта история заставила меня задуматься над критериями тестовых данных и быть более осмотрительным при их подборе, хотя здесь речь шла об этичности данных, которые никак не влияют на качество продукта. Будучи уже лидом, я часто наблюдаю картину, когда тестировщик не особо беспокоится о том, какие тестовые данные использовать в процессе ad-hoc тестирования, что кажется не особо удачной практикой. В этой статье предлагаю немного поразмышлять над качеством тестовых данных.
Статья состоит из двух больших блоков:
Первая — Идеальные данные – какие они? Уникальные, целостные и эквивалентные боевым — с постановкой проблемы. Здесь я говорю о том, как важно иметь данные с боя, рассказываю об опасности рутины и ищу виноватых.
Вторая — «Источники правильных тестовых данных» — с предложением решения. Здесь я рассказываю на примере поля регистрации, на что обращать внимание при самостоятельной генерации данных и о том, какие есть источники для того, чтобы облегчить себе работу, а также расскажу о своем генераторе данных, который помогает с локализацией и привожу текст README, чтобы облегчить генерацию тестовых данных.
Идеальные данные – какие они? Уникальные, целостные и эквивалентные боевым
Постановка проблемы
Многие скажут: спасибо Кэп, мы и так это все знали. К сожалению, знание хороших принципов не приводит к хорошим практикам.
При проверке продукта методом черного ящика правильные входные данные являются залогом успешного тестирования. И в данном случае речь не о граничных значениях или классах эквивалентности, хотя в каком-то смысле данная статья затрагивает эквивалентность, а о семантике данных. Об их непосредственном содержании.
Лучшими данными будут те, которые эквиваленты боевым. Но для тестирования использование боевых данных возможно в очень редких случаях. Законы о персональных данных и требование безопасности – все это накладывает ограничения, которые можно обойти, но риски будут несопоставимо выше профитов.
Например, если вы проверяете эквайринг, то вряд ли будете использовать данные своей личной карты и уже тем более данные чужой карты. Какие же данные использовать? Можно пойти путем простого подбора, при котором номер кредитной карты будет состоять из случайных цифр. Но если ваш продукт сделан качественно, то валидация по алгоритму Луна должна будет отбить ваш номер карты. При этом ваш кейс из позитивного тут же превратится в негативный, что тоже неплохо, но как же быть с позитивным кейсом? Где взять валидный номер карты, кроме своей зарплатной?
В этом то и суть проблемы: получение фейковых данных, эквивалентных боевым, – это нетривиальный процесс, требующий усилий и разумного подхода. В какой-то момент этот процесс становится рутинным и к нему прикладывают все меньше усилий.
В чем опасность рутины?
При тестировании формы регистрации первые разы вы заполняете все поля валидными эквивалентными данными. Но со временем, когда подобную операцию нужно выполнить третий раз в день или сотый раз в неделю, мы все меньше прикладываем усилий к этому «творческому» процессу. Как и в любых рутинных процессах происходит неизбежная деградация. Если вначале наш мозг мог выдать с десяток уникальных ФИО, то постепенно мы зацикливаемся на Василии Пупкине или, что ещё хуже, начинаем вписывать случайные сочетания букв.
Что плохого в таком подходе? Деградация тестовых данных неизбежно приведет к деградации качества тестирования.
Почему это так? Что плохого в «Фывапр Йцукенг Павловна», если рассматривать это как тестовые данные?
Банальные примеры:
Если после регистрации на экране отображается уведомление об успешном прохождении процесса или после успешной авторизации в шапке выводятся полностью ФИО, то как понять, что строка со значениями ФИО не была обрезана внутри нашего «черного ящика»?
Если существует требование о четком порядке ФИО (во многих государственных АИС это жесткое правило), то как обнаружить ошибку, если разработчик сделает неправильный порядок? «Фывапр Йцукенг» и «Йцукенг Фывапр» с точки человеческого восприятия одинаковы, но с точки зрения выходных данных – это баг, который невозможно интуитивно обнаружить.
Таким образом, используя в качестве ФИО рандомные строки, вы сами стреляете себе в ногу.
Пример посерьезнее:
Если при тестировании бухгалтерских систем выбирать слишком маленькие финансовые значения, можно не обнаружить не только ошибки верстки, когда строка будет выходить за зону видимости, но и переполнение памяти, если речь идет о тестировании квартального или годового отчета. Поэтому нужно озаботится адекватными (близкими к боевым) суммами при тестировании, чтобы не обнаружить проблему до начала эксплуатации.
Можно придумать множество других примеров, когда входные данные адекватно не отражают сущности предметной области, что может привести к пропускам более серьезных ошибок, но мне лень. Додумайте сами.
Кроме того, деградация тестовых данных заключается и в их однообразности с точки зрения класса эквивалентности. В какой-то момент Василий Пупкин попадет под действие «эффекта пестицида» и баги, живущие в вашей системе, постепенно «адаптируются» к нему. Тогда Вася Пупкин станет не лучшим другом, а злейшим врагом.
Кто же виноват?
Что же мешает каждый раз идти путем создания новых качественных данных? Ответ прост – лень. Как известно из исследований по нейробиологии, мы с трудом заставляем себя думать, когда речь идет о рутинных процессах. Подробнее об этом эффекте интересно написано в книге «Думай медленно… решай быстро» Даниэля Канемана. Автор так описывает закономерность умственной работы в любой сфере:
«По мере того как вы приобретаете новый навык, он требует все меньше энергии. Исследования показывают, что со временем при исполнении действия активизируется все меньше участков мозга. Сходное действие и у таланта. Люди с высоким интеллектом тратят меньше сил на решение заданий, на что указывает и размер зрачков, и активность головного мозга. И к физическим, и к умственным усилиям применяется один и тот же «закон наименьшего напряжения». Согласно ему, из нескольких вариантов достижения одной цели люди в конечном итоге всегда склоняются к наименее затратному. В экономике действия усилие – это затраты, а получение навыков уравновешивает соотношение затрат и выгод. Лень – неотъемлемая часть нашей натуры»
Наш мозг, как источник тестовых данных, с одной стороны является нашим союзником на короткие дистанции, а с другой — нашим врагом на длинные.
Оказывается, что придумывать новые данные гораздо сложнее, чем использовать какие-то существующие шаблоны. Наш мозг привык идти по пути наименьшего сопротивления, потому-то мы обречены циклически обходить наши шаблоны и использовать их в качестве тестовых данных, что само по себе может привести к «эффекту пестицида».
Кто-то может сказать, что несложно нагенерировать в своем сознании сотню уникальных ФИО. И с этим действительно вряд ли будут проблемы. Но что если нужно сгенерировать более сложные сущности, состоящие из десятка атрибутов, которые имеют строгие логические и семантические связи? Готов поспорить, что вы быстро деградируете в этом процессе на длинной дистанции, если только ваш лид не будет стоять за спиной, стимулируя вашу мозговую деятельность.
Итак, нам нужны хорошие тестовые данные, которые будут эквиваленты боевым. Генерация таких данных – процесс нетривиальный и во многом рутинный. Если с вопросом «кто виноват?» уже примерно разобрались, то нужно задаться следующим вопросом: «что делать?».
Источники правильных тестовых данных
Что такое «правильные» тестовые данные?
Любая АИС является представлением реального мира, и в ней мы взаимодействуем с его информационными моделями реального мира. Чем ближе эта модель к реальному прототипу, тем выше шансы найти важный дефект. Так, например, при регистрации пользователя мы не просто заполняем поля формы, но создаем некую сущность, которая должна отражать реальность.
Лучшим подходом будет рассматривать данные регистрации как некую целостную сущность, которая отражает часть реального мира, т.е. некую персону. У персоны есть пол, дата рождения и ФИО. Все эти данные логически и семантически связаны друг с другом.
Вряд ли, отправляясь в ЗАГС для получения свидетельства для своей новорожденной дочери, вы выберите ей имя «Фывапр Йцукенг Павловна». Вряд ли вы назовете свою дочь мужским именем.
Вряд ли дата рождения персоны может быть более 100-110 лет.
При таком подходе информационная модель данных далека от реальности, а сами атрибуты имеют слабые логические и семантические связи. Наилучшим подходом будет сущность, имеющая явно выраженные логические связи между атрибутами, которая адекватная реальности.
Речь идет о том, что ФИО должны соответствовать полу персоны, а год рождения должен быть в некоем адекватном диапазоне. Если копнуть еще глубже, то можно выявить корреляцию между датами рождения и неким множеством имен, которые были популярны в эти годы. Подобными статистическими закономерностями можно пренебречь, т.к. их отсутствие критически не уменьшит логическую связь.
Чем могут помочь открытые источники данных?
Как вы поняли, наш мозг — хороший источник тестовых данных, но обладает рядом недостатков.
Любые рутинные процессы с четким алгоритмом должны быть автоматизированы. Процесс генерации тестовых данных — не исключение. Давайте посмотрим, какие источники тестовых данных сегодня есть.
Нейросети. Только ленивый уже не писал про ИИ для ИТ-сферы. Возможности тестовых ИИ и вправду поражают. По запросу они могут создавать или преобразовывать целые массивы специфицированных данных. Проблема в том, что эти данные нуждаются в проверке и не могут быть приняты как корректные по умолчанию.
Биржи данных. Это площадки, где можно легально пробрести данные из различных предметных областей. Данные, как правильно, деперсонифицированы, но при этом наиболее близки к боевым. По своей сути такие данные являются целостной моделью реальных сущностей. Это их главный плюс. А минусы в том, что нужно платить за это, и они неизбежно будут устаревать. Такие данные можно использовать как при ручном тестировании, так и в автоматизированном. Например, наполнять ими заглушки, тестовые БД или использовать для генерации данных.
Сервисы данных. По своей сути это веб-сайты, на которых можно сгенерировать данные нужного вам типа.
Вот несколько примеров:
https://generatedata.com/
https://www.random1.ru/
https://www.vccgenerator.org/
Это отличный вариант при использовании в ручном тестировании. Благо, что существуют множество вариантов с данными, которые локализованы под российские проекты. Но проблема в том, что многие тестовые стенды находятся в закрытых контурах и перенос данных туда будет затруднен. Не говоря о том, что машиночитаемый формат будет опосредован, что затруднит использование при автотестировании.
Библиотеки генерации
Это программные модули, которые можно подключить к существующему проекту, и получать их данные для использования в юнит-, автотестах или наполнения заглушек.
Например, JavaFaker. Эта библиотека с открытым кодом, которая имеет достаточно обширный набор предметных областей, для которых можно генерировать данные. Недостаток в том, что нет данных валидных для российских проектов. Например, в списке типов кредитных карт нет платежной системы МИР, да и вряд ли когда-нибудь будет.
В таком случае, мы сами можем допиливать эту библиотеку под свои нужды. Но это немногие захотят делать!
Есть другой вариант – написание свой библиотеки, которая будет иметь локализацию для российский проектов. Задавшись такой целью, я создал свою библиотеку, которая в общем и целом закрывает вышеописанные проблемы.
Свой велосипед для данных!
pro.dagen.datagenerator - https://github.com/upaul23/DataGenerator
Это библиотека с открытым кодом, цель которой – генерировать данные, эквивалентные боевым, подходящие под нужды автоматизаторов и тестировщиков, работающих на российских проектах.
Поскольку, по чистой случайности, автор статьи и автор библиотеки один тот же человек, то расскажу про нее чуть подробнее.
Есть множество библиотек для Java, но все они выполняют узкие задачи или не выдают данные, подходящие для российских проектов. Но для нужд тестирования эти данные очень нужны.
Библиотека реализует все принципы, описанные выше, все данные генерируются семантически верными и проходящими формальную валидацию.
Например, все номера банковских карт проходят валидацию по алгоритму Луна или имеют БИН реального банка, кроме случаев, когда у банка отсутствует подходящий тип карты.
С другой стороны, библиотека выдает несуществующие почтовые адреса, которые вряд ли пройдут валидацию по ГАР. Однако если вы используете вместо адресного сервиса заглушку для валидации адресов, то библиотека поможет вам наполнить ее семантически верными данными.
Ниже описание самой библиотеки:
Описание DataGenerator
Ниже текст из README самой библиотеки, в котором описан ее функционал.
DATAGENERATOR
Данная библиотека предназначена для генерации случайных данных, специфицированных под русскоязычные проекты. Данные аналогичны реальным, боевым данным.
Библиотека не использует чьих-то персональных данных. Все данные являются случайно сгенерированными (кроме случаев кастомной генерации) и любые совпадения с реальными персональными данными являются вероятностным совпадением.
Все реальные данные взяты из открытых источников и не нарушают права третьих лиц.
Методы генерации не являются идемпотентными, т.е. каждый последующий вызов метода будет возвращать уникальные данные. В качестве тезауруса используются списки, хранящиеся в тестовых файлах в папке resource/dictionary. Классы генераторов получают данные о расположении файлов из dagen.properties по соответствующим ключам.
Генерация данных происходит по алгоритму случайных выборок из тезауруса и комбинирования данных для создания сущности. Класс DataGenerator – является точкой входа и содержит статические методы, для получения данных из соответствующих генераторов.
Библиотека генерирует следующие данные:
Персоны
Паспортные данные
СНИЛС
Расчетный счет банка
Банковские данные
ИНН для ФЛ
ИНН для ЮЛ
ОГРН для ЮЛ
Номер мобильного телефона
Номер городского телефона
Адрес электронной почты
Автомобильные гос. номера
Реквизиты банковских карт
Примеры использования:
//Персоны, отличные от Васи Пупкина
FakePerson fakePerson = DataGenerator.persons().get();
FakePerson malePerson = DataGenerator.persons().get(Gender.MALE);
//Паспортные данные
FakeRussianPassport fakeRussianPassport = DataGenerator.documents().passport();
//Банковский счет
FakeAccount accountNumber = DataGenerator.accountDetails().account(
PersoneType.PERSON,
Currency.RUB,
ProfileType.COMMERCIAL,
DataGenerator.accountDetails().bank()
);
//Почтовый адрес
FakeAddress fakeAddress = DataGenerator.address().address();
//Банковская карта
FakeCard fakeCard = DataGenerator.bankCard().card(
Banks.SBER,
CardType.MIR,
//Имя держателя карты. При генерации будет транслитерировано в латинские буквы
"Василий Пупкин"
);
//Автомобильные гос. номера
FakeCarStateNumber fakeCarStateNumber = DataGenerator.cars().stateNumber();
//Контакты
FakePhoneNumber fakePhoneNumber = DataGenerator.contacts().mobile();
FakePhoneNumber fakePhoneNumber = DataGenerator.contacts().cityPhone();
String email = DataGenerator.contacts().email("test.ru");
Как подключить библиотеку?
Добавить в pom.xml вашего проекта, как зависимость:
<dependency>
<groupId>pro.dagen</groupId>
<artifactId>datagenerator</artifactId>
<version>1.2.0</version>
</dependency>
Несмотря на то, что библиотека новая, ее код уже многие годы используется на реальном проекте. Не исключаю, что она может содержать некоторые проблемы и ошибки, которые еще не были найдены. Буду очень благодарен, если дадите конструктивную обратную связь или заведете тикет на улучшение в https://github.com/upaul23/DataGenerator.
Вместо заключения
В итоге, каким бы способом генерации данных вы не пользовались, важно использовать данные, близкие к «боевым». Следуя этому принципу, вы уменьшите риск пропуска багов в продакшен и, конечно же, сможете повысить качество продукта. Неважно, каким способом вы тестируете — ручным или автоматизированным. В обоих подходах главное - придерживаться критериев уникальности, целостности и эквивалентности боевым данным. К счастью, сегодня есть множество инструментов, которые могут с этим помочь. Часть таких решений была представлена в данной статье. Хочется надеется, что после ее прочтения вы задумаетесь над вопросом подбора тестовых данных, а помимо Васи Пупкина, у вас появятся еще больше тестовых «друзей».
на улучшение в https://github.com/upaul23/DataGenerator.