Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

Привет, Хабр!

Меня зовут Максим Пономаренко и я — разработчик в Спортмастере. Имею 10-летний опыт работы в IT-сфере. Начинал карьеру в в области ручного тестирования, затем переключился на разработку баз данных. Последние 4 года, аккумулируя знания, полученные в тестировании и разработке, занимаюсь автоматизацией тестирования на уровне СУБД.

В команде Спортмастера я состою чуть более года и на одном из крупных проектов занимаюсь разработкой автоматизированного тестирования. В апреле мы с ребятами из Sportmaster Lab выступали на конференции в Краснодаре, мой доклад назывался «Unit-тесты в СУБД», и сейчас хочу поделиться им с вами. Текста будет много, поэтому я решил разбить доклад на два поста. В первом мы поговорим об автотестах и тестировании в общем, а во втором я подробнее остановлюсь на нашей системе unit-тестирования и результатах ее применения.

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

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

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

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

Но что делать, если ваша система основывается преимущественно на серверной логике? Какого-то универсального решения и best practices на рынке не существует. Как правило, компании решают эту проблему за счет создания собственной самописной системы тестирования. Вот такая собственная самописная система автоматизированного тестирования была создана на нашем проекте и о ней я расскажу в своем докладе.

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

Тестируем лояльность

Для начала поговорим о проекте, где мы развернули систему автоматизированного тестирования. Наш проект — это система лояльности Спортмастера (кстати, мы уже писали про нее в этом посте).

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

  • Ваша система будет высоконагруженной
  • Ваша система будет содержать сложные вычислительные процессы
  • Ваша система будет активно дорабатываться.

Пойдём по порядку… В совокупности, если рассматривать все бренды Спортмастера, то на территории России, Украины, Китая, Казахстана и Беларуси у нас более 1000 магазинов. В этих магазинах ежедневно осуществляется около 300 000 покупок. То есть каждую секунду в нашу систему попадает 3-4 чека. Естественно, наша система лояльности является высоконагруженной. А раз ею активно пользуются, мы должны предоставлять самые высокие стандарты ее качества, ведь любая ошибка в ПО — это большие денежные, репутационные и прочие потери.

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

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

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

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

Технологически на 90% логика нашей системы лояльности серверная и реализована на Oracle. Есть выставленный клиент на Delphi, который выполняет функцию АРМ-администратора. Есть выставленные веб-сервисы для внешних приложений (например веб-сайт). Поэтому весьма логично, что если мы будем разворачивать систему автоматизированного тестирования, то будем делать это на Oracle.

Система лояльности в Спортмастере существует более 7 лет и создавалась единичными разработчиками… Среднее количество разработчиков на нашем проекте в течение этих 7 лет составляло 3-4 человека. Но за последний год наша команда сильно увеличилась, и теперь над проектом трудятся 10 человек. То есть в проект приходят люди, которые не знакомы с типовыми задачами, процессами, архитектурой. И есть повышенный риск того, что мы будем пропускать ошибки.

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

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

На помощь приходит utPLSQL

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

Знаете что-нибудь про Стивена Фейерштейна?

Это умный дядька, который длительную часть своей карьеры посвятил работе с Oracle и с PL/SQL, написал на эту тему достаточно большое количество трудов. Одна известная его книга так и называется: «Oracle PL/SQL. Для профессионалов». Именно Стивену принадлежит разработка решения utPLSQL, или, как оно расшифровывается, Unit Testing framework for Oracle PL/SQL. Решение utPLSQL было создано в 2016 году, но над ним продолжают активно работать и выпускать новые версии. На момент доклада последняя версия датируется 24 мартом 2019 года.
Что же это такое. Это отдельный опенсорный проект. Весит пару мегабайт с учётом примеров и документации. Физически представляет собой отдельную схему в базе данных ORACLE с набором пакетов и таблиц для организации юнит-тестирования. Установка занимает несколько секунд. Отличительной особенностью utPLSQL является простота эксплуатации.
Глобально, utPLSQL представляет собой механизм для запуска юнит-тестов, где под юнит-тестом понимаются обычные оракловые пакетные процедуры, организация которых соответствует некоторым правилам. Помимо запуска в utPLSQL хранится лог всех ваших тестовых запусков, а также есть внутренняя система отчётности.

Давайте посмотрим на примере, как выглядит код unit-теста, реализованный по данной методике.

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

Итак, на экране представлен код типовой спецификации пакета с unit-тестами. Какие есть обязательные требования? Пакет должен иметь префикс «utp_». Точно такой же префикс должны иметь все процедуры с тестами. В пакете в обязательном порядке должны присутствовать две стандартные процедуры: «utp_setup» и «utp_teardown». Первая процедура вызывается перезапуском каждого unit-теста, вторая — после запуска.

«utp_setup», как правило, подготавливает нашу систему к запуску unit-теста, например, создает тестовые данные. «utp_teardown» — наоборот, все возвращает к исходным настройкам и сбрасывает результаты запуска.

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

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

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

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

Вот так unit-тесты запускаются. Допустимо два варианта запуска: запуск всех юнит-тестов из конкретного пакета или запуск конкретного юнит-теста в конкретном пакете.

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

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

6 правил автотестов

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

Unit-тесты в СУБД — как мы делаем это в Спортмастере, часть первая

  1. Автотесты должны быть эффективными и должны приносить пользу. У нас замечательные разработчики, о которых обязательно надо сказать, ведь кто-то из них наверняка увидит этот доклад, и они пишут замечательный код. Но даже их замечательный код не является идеальным и содержал, содержит и будет содержать ошибки. Автотесты обязаны эти ошибки находить. Если это не так, то либо мы пишем плохие автотесты, либо мы пришли в мертвую область, которая в принципе не дорабатывается. В обоих случаях мы делаем что-то не так, и наш подход просто бессмысленный.
  2. Автотесты должны использоваться. Бессмысленно потратить кучу времени и сил на написание программного продукта, сложить его репозитарий и забыть. Тесты должны запускаться, и запускаться как можно регулярнее.
  3. Автотесты должны работать стабильно. Вне зависимости от времени суток, стенда запуска и прочих настроек системы, запуски тестов должны приводить к одному и тому же результату. Как правило, это обеспечивается тем, что автотесты работают со специальными тестовыми данными с зафиксированными настройками системы.
  4. Автотесты должны работать с приемлемой для вашего проекта скоростью. Данное время определяется индивидуально для каждой системы. Кто-то может себе позволить работать целый день, а кому-то критично укладываться в секунды. Каких скоростных нормативов мы добились в нашем проекте, я расскажу чуть позже.
  5. Разработка автотестов должна быть гибкой. Нежелательно отказываться от проверки какого-либо функционала просто потому, что мы так ещё не делали или по каким-то другим убеждениям. utPLSQL не накладывает никаких ограничений на разработку, а Oracle в принципе позволяет реализовывать самые разные вещи. Большинство задач имеет решение, вопрос только во времени и потраченных усилиях.
  6. Развёртываемость. У нас несколько стендов, где нужен запуск тестов. На каждом из стендов в любой момент может быть обновлён дамп с данными. Нужно вести проект с автотестами таким образом, чтобы иметь возможность безболезненно производить его полную или частичную установку.

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

Источник: habr.com