что не может быть использовано как тест oracle
Оракулы часто работают отдельно от тестируемой системы. Однако постусловия метода являются частью тестируемой системы как автоматизированные оракулы при проектировании с помощью контрактных моделей. Определение правильного вывода для заданного входа (и набора состояний программы или системы) известно как проблема оракула или проблема тестового оракула, что намного сложнее, чем кажется, и включает работу с проблемами, связанными с управляемостью и наблюдаемостью.
СОДЕРЖАНИЕ
Категории
Обзор исследовательской литературы, охватывающий период с 1978 по 2012 год, выявил несколько потенциальных категорий тестовых оракулов.
Указано
Указанные тестовые оракулы имеют ряд проблем. Формальная спецификация основана на абстракции, которая, в свою очередь, может иметь элемент неточности, поскольку все модели не могут уловить все поведение.
Полученный
Псевдо-оракул попадает в категорию производного тестового оракула. Псевдо-оракул, по определению Вейукера, представляет собой отдельно написанную программу, которая может принимать те же входные данные, что и тестируемая программа или система, так что их выходные данные можно сравнивать, чтобы понять, есть ли проблема для исследования.
Скрытый
Человек
Если указанные, производные или неявные тестовые оракулы не могут быть использованы, то для определения тестовых оракулов требуется участие человека. Их можно рассматривать как количественный и качественный подходы. Количественный подход направлен на поиск нужного количества информации, которую нужно собрать о тестируемой системе (например, результатов тестирования), чтобы заинтересованная сторона могла принять решение о соответствии или выпуске программного обеспечения. Качественный подход направлен на определение репрезентативности и пригодности входных тестовых данных и контекста выходных данных тестируемой системы. Примером может служить использование реалистичных и репрезентативных данных испытаний и понимание результатов (если они реалистичны). При этом можно руководствоваться эвристическими подходами, такими как интуиция, эмпирические правила, вспомогательные контрольные списки и опыт, чтобы помочь адаптировать конкретную комбинацию, выбранную для тестируемой программы / системы.
Примеры
Оракул согласованности сравнивает результаты выполнения одного теста с другим на предмет сходства. Это еще один пример производного тестового оракула.
Оракул для программы может быть второй программой, которая использует другой алгоритм для вычисления того же математического выражения, что и тестируемый продукт. Это пример псевдо-оракула, который является производным тестовым оракулом.
Во время поиска Google у нас нет полного оракула, чтобы проверить правильность количества возвращенных результатов. Мы можем определить метаморфическое отношение таким образом, чтобы последующий суженный поиск давал меньше результатов. Это пример частичного оракула, который представляет собой гибрид указанного тестового оракула и производного тестового оракула.
Статистический оракул использует вероятностные характеристики, например, с анализом изображений, где определен диапазон достоверности и неопределенности для того, чтобы тестовый оракул произнес совпадение или иначе. Это был бы пример количественного подхода в человеческом тестовом оракуле.
Эвристический оракул предоставляет репрезентативные или приблизительные результаты по классу тестовых входных данных. Это был бы пример качественного подхода в человеческом тестовом оракуле.
Как мы используем юнит-тестирование в СУБД Oracle
В некоторых технологиях и языках программирования юнит-тестирование — уже давно неотъемлемая часть написания кода. Оно интегрировано в разработку и доступно «из коробки» в виде фреймворков, как, например, JUnit для Java, xUnit/nUnit для C# и т. д. Но в Oracle культура юнит-тестирования мало распространена. В статье я расскажу, как и зачем мы внедрили автотесты при разработке на Oracle и для чего их используем.
На одном из проектов мы автоматизируем деятельность крупнейшего российского ритейлера. Большая часть софта написана на PL/SQL, процедурном расширении SQL, которое используется в Oracle. Помимо стандартной транзакционной логики СУБД, в нем много интеграций с внешними системами по SOAP и REST. Как мы выстраиваем эти интеграции, можно почитать тут.
Раньше вся работа по тестированию на проекте ложилась на плечи инженера. Но его ресурсы ограничены и не позволяют проводить регрессионное тестирование всего приложения при добавлении небольшой фичи или исправлении бага. Поэтому в коде потом появлялись ошибки, которые становились головной болью для всех.
Мы решили, что юнит-тестирование поможет снизить количество багов и вообще уменьшить масштабы ручного труда.
Какой фреймворк мы выбрали
В качестве эксперимента мы решили автоматизировать тестирование одной из небольших подсистем с помощью open source фреймворка utPLSQL и в случае успеха распространить практику на остальную кодовую базу.
Чем хорош фреймворк для нас:
близость по концепции и принципу действия к классическим фреймворкам в других языках программирования;
большое сообщество пользователей, распространенность, периодическая актуализация и исправление ошибок в новых версиях;
обширный функционал, полностью покрывающий наши потребности.
По сути, альтернатив этому фреймворку для работы с Oracle нет. Некоторые разработчики пишут свои скрипты для тестирования, но довольно затруднительно использовать их на проекте с большой кодовой базой.
Опустим рассказ об установке фреймворка и его основных возможностях, на эту тему достаточно материала и примеров в открытых источниках. Сосредоточимся на том, как именно мы применяем utPLSQL в наших реалиях.
Как работает фреймворк на проекте
Общий принцип
Мы частично принесли в жертву модульность и не создаем для каждой функции по набору позитивных и негативных тест-кейсов. В первую очередь мы тестируем комплексные процессы, отражающие реальные бизнес-цепочки событий заказчика. Почему так?
Во-первых, на экспериментальном этапе с нуля описывать юнит-тестами каждую функцию и процедуру слишком долго и дорого, на это не всегда хватает ресурсов, а результаты хочется получить как можно быстрее.
Во-вторых, нам куда важнее убедиться, что комплексный процесс выполняет свою роль в среде эксплуатации согласно бизнес-модели, чем проверять работоспособность вспомогательных атомарных методов.
Схема обмена данными при тестировании
Наши продукты имеют версии. Каждая версия слоя базы данных приложения — это набор скриптов, пакетов, процедур и функций, измененных в текущем релизе и собранных в один большой исполняемый SQL-скрипт. Версия загружается в систему-хаб распространения обновлений и оттуда устанавливается на конкретные серверы, где происходит тестирование. Схематично это изображено на рисунке ниже.
После тестирования версия загружается в аналогичную систему заказчика с доступом к боевым серверам. Для тестирования, помимо «железных серверов», на которых развернуты инсталляции, у нас налажена работа с так называемыми TimeMachine-клонами. Если коротко, этот механизм — микс из GitLab, OpenNebula, Ceph и других инструментов, который позволяет быстро по кнопке или по расписанию поднимать инсталляции нужных версий на виртуальных серверах, используя user-friendly интерфейс GitLab. Эти инсталляции изначально использовались для ручного тестирования, но после внедрения юнит-тестирования на них еще прогоняются все автотесты с отбивкой и алертами об успешном или неуспешном выполнении. Мы также сохраняем историю запуска тестов. Это позволяет выявить, в какой момент и при каких правках был сломан тот или иной алгоритм в коде.
Пример бизнес-процесса
При реализации такого бизнес-процесса мы обычно применяем автотесты. Дано: загрузка и обработка заказа из интернет-магазина.
Загружаем заказ покупателя на доставку из внешней системы.
Обрабатываем заказ на нашей стороне, проверяем, что такого заказа еще нет, проверяем данные покупателя.
Добавляем данные из другой внешней системы, отвечающей за спецификацию заказа, оплаты, резервы и прочее.
В зависимости от способа доставки заказа можем синхронизировать состояние с системой, которая отвечает за взаимодействие с курьерскими компаниями или собственными экспедиторами. На этом этапе, если все прошло гладко, заказ завершается.
В процессе создается большое количество учетных документов, имеющих разную направленность и адресатов: корзины, накладные, операции, документы оплаты, акты расхода и т. п. Все это нужно проверить на корректность и соответствие требованиям.
Вариантов развития у описанного бизнес-процесса может быть много. Клиент может отказаться, выкупить только часть заказа, доставка может быть платной, бесплатной или вообще отсутствовать, предоплата может быть полной, частичной, ее может не быть совсем и т. д.
Как тестирование реализовано в коде
Масштаб теста — один пакет (назовем его ut_order), по одной процедуре для каждого сценария, процедуры beforeeach/aftereach для подготовки и обнуления тестовых данных и сброса кэша.
При большой вариативности сценариев бизнес-процесса количество и объем юнит-тестов будут весьма существенны. Если покрывать тестированием каждую задействованную функцию и процедуру, можно потратить очень много времени, при этом так или иначе все равно придется «склеивать» их в имитации процесса. Добавим сюда необходимость актуализировать и адаптировать функции и процедуры под постоянно меняющиеся бизнес-требования. Что касается самой методологии тестирования, все эти потребности покрываются встроенными механизмами фреймворка — annotations и expectations.
Полноценный тестовый инфообмен с внешними системами в нашем случае невозможен, так как это рабочие сервера заказчика. Поэтому для эмулирования обращений к таким системам решено было создать таблицу, в которой лежали бы сгенерированные запросы и ожидаемые ответы. Таким образом, при тестировании в местах бизнес-процесса, где должен происходить информационный обмен с внешними сервисами, мы вместо реального обращения к ним кладем в таблицу запрос и из нее же забираем данные.
Разберем реализацию тестирования в одном из сценариев бизнес-процесса.
Тестовый пакет, в котором собраны тест-кейсы:
Эти аннотации позволяют фреймворку понять, что данный пакет является тестовым, и выставить определенные параметры, позволяющие сделать тестирование гибким и удобным. Полный список аннотаций с подробным описанием и примерами можно найти в мануале на сайте фреймворка.
Тестовая процедура в пакете, помеченная аннотацией:
Разного рода генераторы номеров заказов, накладных и прочего у нас вынесены в отдельный вспомогательный пакет. Кладем в mock-таблицу препарированный ранее запрос, выставляем системный параметр, от которого зависит, будут ли пакеты с логикой ссылаться на табличку или же формировать настоящий запрос.
В пакетах с логикой имитируем получение ответа от сервиса, используя данные из тестовой таблицы:
Проверяем все остальные созданные документы (корзины, накладные, документы оплаты и пр.).
Как запускаются автотесты
Как уже было сказано, запуск происходит не вручную в базе данных, а через настроенный механизм с интерфейсом в GitLab CI. Его возможности позволяют настроить интервальный запуск (Schedules) тестов, который включает в себя несколько шагов:
Клонируем инсталляцию с эталонного стенда с последней «боевой» (отданной заказчику) версией на борту.
Обновляем стенд до текущей рабочей версии из системы управления версиями.
Создаем (пересоздаем) объекты, необходимые для работы фреймворка, — пользователя, таблицы, пакеты, процедуры и т. п.
Прогоняем скрипт с пакетами, в которых реализованы юнит-тесты. Актуальные версии пакетов хранятся и выкачиваются из Git, то есть программисту достаточно добавить в репозиторий новый пакет или внести правки в существующий — и при запуске всегда будет использоваться актуальная версия.
Для запуска тестов используется штатный функционал фреймворка (команда ut.run()). Он же генерирует лог выполнения. Схематично весь этот процесс изображен на рисунке ниже.
Кроме того, в GitLab CI есть возможность разового запуска тестов на выбранных инсталляциях, в таком случае выполняются лишь последние два шага. Вот как это выглядит в интерфейсе:
Все это реализовано «под капотом», и инженеру достаточно выбрать существующую инсталляцию или создать новую и нажать буквально пару кнопок. Результат можно наблюдать в логах работы задания там же в GitLab.
Как видим, в результате прогона наших тестов что-то пошло не так. Судя по логу, 7 из 28 тестов завершились неуспешно. Теперь можно открыть пакет в нашей инсталляции и по приложенному стеку проследить, где и почему возникла логическая ошибка или системное исключение.
По всем запускам — успешным и неуспешным, настроенным по расписанию или ручным — приходит отбивка в специальный канал в рабочем мессенджере, на который подписаны инженеры и разработчики. В случае ошибок разбираемся и правим вручную ошибочные места в коде.
Заключение
В целом стоит отметить, что наша модель работы с юнит-тестами получилась весьма эффективной и позволяет оперативно отслеживать возникновение ошибок и багов. По результатам внедрения автотестов, общий среднегодовой показатель cost of bugs для конкретного продукта снизился с 26,22% до 19,14%, по покрытым тестами бизнес-процессам проблемы стали возникать гораздо реже, чем раньше.
Сейчас, когда основные процессы запуска и мониторинга отлажены и данная модель признана командой успешной, перед нами стоит главная задача — повышать покрытие остальных процессов и продуктов. Надеюсь, наш опыт поможет и другим командам наладить процессы юнит-тестирования в СУБД Oracle, ведь на рынке остается много продуктов, в которых так или иначе задействован хранимый код PL/SQL и нет внятной стратегии, как наладить его автоматическое тестирование.
TDD для хранимых процедур Oracle
На одном из наших недавних проектов мы столкнулись с серьёзной проблемой. Веб-приложение, которое мы разрабатывали, должно было использовать внутренюю базу данных финансовой организации. Из соображений безопасности, доступ был очень сильно ограничен: любые изменения необходимо было делать при помощи хранимых процедур, а читать данные — только при помощи представлений. Таким образом, приложение должно было выполнять сложные манипуляции данными, не имея никакого представления об их структуре. Основной загвоздкой для нас было то, что наше приложение попадало в зависимость от больших и сложных процедур, для которых не существовало автоматизированных тестов.
Погуглив немного, мы обнаружили, что в штатном инструментарии Oracle SQL Developer [1] есть функционал для создания автоматизированных тестов. Мы тут же приступили к его изучению. И хотя тесты для самой сложной процедуры пришлось создавать уже после её написания, этот инструментарий всё же помог нам устранить несколько ошибок, а также существенно облегчил процесс расширения функционала и рефакторинга. Ниже я приведу пример использования TDD для построения хранимых процедур, а также поделюсь опытом в работе с инструментарием.
Пример использования
Допустим, у заказчика имеется существующее приложение, которое позволяет его клиентам выполнять отправку СМС-сообщений. Ещё одна команда разрабатывает новое приложение, которое должно будет работать параллельно с существующим, поэтому было бы хорошо иметь общее место для бизнес-логики.
Структура данных
Приложение использует следующую структуру данных:
Для краткости, определения первичных и внешних ключей опущены.
Настройка окружения
Юнит-тестирование в SQL Developer использует базу данных для хранения тестов, их настроек, библиотеки, и результатов выполнения. В этих целях настоятельно рекомендуется создать пользователя для тестирования, затем создать в его базе данных репозиторий. Этот процесс более подробно описан в документации по юнит-тестированию [2].
Терминология тестирования Oracle
Терминология тестирования, которую использует Oracle несколько отличается от общепринятой терминоголии xUnit [3]:
| xUnit | SQL Developer | Комментарий к SQL Developer |
|---|---|---|
| Набор тестов | Test Suite | Может включать другие наборы тестов и/или сценарии |
| Тестовый сценарий | Test | Может тестировать только одну функцию или процедуру |
| Тест | Test Implementation | |
| Настройка контекста (setup) | Startup Process | Доступна на уровне теста и набора тестов |
| Сброс контекста (tear down) | Teardown Process | см. выше |
Далее в тексте я буду использовать русский вариант терминологии xUnit.
Неожиданности
Работая с приложением, мы обнаружили, что оно не всегда работает так, как мы ожидали:
Разработка с помощью тестирования
Прежде чем мы сможем начать, необходимо создать пустую процедуру, а иначе невозможно будет создать тест. И хотя список аргументов можно оставить пустым, в этом нет никакой необходимости.
Изначально, мы можем предположить, что для того чтобы отправить сообщение, нам будет необходим идентификатор клиента, отправитель, получатель, а также тело самого сообщения. Также, нам необходимо сигнализировать результат выполнения, скажем, через выходной параметр. При помощи диалога создания процедуры можно получить вполне подходящее определение:
В случае с Oracle, имеет смысл задавать префикс для переменных, имя которых может совпасть с названием поля, так как в случае неясности, знаменитая СУБД решит спор в пользу поля. А во избежании беспорядка, проще давать префикс всем переменным без исключения.
Если параметры процедуры изменились, то каждый её тестовый сценарий необходимо обновить вручную, щёлкнув пункт контекстного меню Synchronize Test.
Первый сценарий
Для упрощения нашего примера, допустим, что стоимость одного сообщения — 0.03 каких-то денег. И, как это ни странно, для описания сценария вполне подходит Gherkin:
Настройка контекста
Сначала нам необходимо будет заполнить базу необходмыми данными. Для нас самым удобным оказалось использование режима PL/SQL для настройки и сброса контекста. Тем не менее, любой из вариантов легко использовать повторно при помощи публикации в библиотеку. Чтобы скопировать существующий шаг из библиотеки, достаточно выбрать его из выпадающего списка, затем нажать кнопку Copy. А если нужно использовать его без изменений, но вместо кнопки Copy необходимо нажать чекбокс Subscribe.
Идея использовать существующую БД для тестирования может показаться привлекательной. Казалось бы, сохранил данные в настройке, и восстановил при сбросе контекста… Однако следует иметь в виду, что если в процессе выполнения тестов на любом этапе произошла непредвиденная ошибка, то база данных окажется в том виде, в каком она была во время ошибки, и сброс контекста выполнен не будет. Поэтому лучше всего использовать чистую базу данных, которую не страшно и несложно полностью пересоздать в случае порчи структуры или данных.
Предполагая, что мы работаем с пустой базой данных, для настойки контекста, нам понадобится всего лишь одна вставка записи пост-оплатного клиента. Её можно тут же сохранить в библиотеке, назвав Пост-оплатный клиент.
Сброс контекста
Чтобы иметь возможность повторного прогона тестов, необходимо очистить добавленные данные. Однадко, в нашем случае, можно просто очистить все таблицы, затрагиваемые тестами. Этот шаг также нужно сохранить в библиотеку для дальнейшего использования.
Вызов
Непосредственно выполнение теста определяется при помощи задания параметров хранимой процедуры. Здесь же задаются и значения выходных параметров для проверки. Проверку выходных параметров можно отключить при помощи чекбокса Test Result. Он относится к параметрам, заданным как в таблице, так и динамически.
С виду может показаться, что задавать параметры мышкой в таблице очень удобно, однако необходимо иметь в виду, что эта таблица копированию не подлежит. Это особенно важно для процедур с большим количеством аргументов, так как для создания очередного теста их все придётся заново задавать вручную, особенно когда новый тест отличается от текущего всего лишь на одно значение. Динамический запрос (Dynamic Value Query), в отличие от таблицы, можно сохранять в библиотеке, а затем можно либо повторно использовать, либо копировать.
Чтобы вернутся из режима динамического запроса в табличный, необходимо полностью очистить значение динамического запроса.
Так как мы указали проверку выходного параметра, то уже можно запустить сценарий, и увидеть сбой. Если всё сделано правильно, система должно сообщить об ошибке. Любой другой сбой на этом этапе означает некорректную настройку.
Самый простой способ успокоить тест — внаглую вписать 1 в выходной параметр в теле процедуры: SELECT 1 INTO IS_QUEUED FROM DUAL;
Утверждения
Тест снова зелёный, но мы ещё не проверили все необходимые условия. Их можно проверить в других тестах того же сценария. Перед тем как создавать новый тест, стоит переименовать существующий из дефолтного «Test Implementation 1» в «Положительный результат», а весь сценарий — в «Активный пост-оплатный клиент отправляет сообщение».
Легко предположить, что каждый тест выполняется внутри транзакции. Однако на поверку это оказалось не так. В случае возникновения непредвиденной ошибки, база данных может оказаться в непределённом состоянии. Ожидаемых ошибок такое поведение не касается.
Наша следующая проверка будет помещена в отдельный тест для получения более тонкой обратной связи, однако, стоит помнить, что каждый новый тест будет затрачивать время на настройку и сброс контекста, а каждый сбой проверки снабжается чётким сообщением о его причине. Мы разделим проверки по разным тестам в этом сценарии, а потом объединим все проверки в один тест в следующем сценарии.
SQL Developer не позволяет просматривать два теста одновременно. При переходе к другому тесту в дереве, текущий тест заменяется новым в той же панели. Помимо того, невозможно разбить эту панель на две независимо прокручиваемые области. Однако, очень удобно открыть исходный код процедуры параллельно с окном теста для быстрого перехода между двумя панелями.
Следующий тест должен проверить, что сообщение помещено в очередь. Так как настройка и сброс контекста уже указаны, нам необходимо использовать динамический запрос из библиотеки, и задать проверку утверждения. После того как мы скопировали динамический запрос, может показаться, что проверять уже проверенный выходной параметр ни к чему, и можно сбросить чекбокс Test Result. Однако, если прогнать тесты в таком состоянии, то будет видно, что один из тестов проигнорирован. Лично для меня проигнорированный тест — символ незаконченной работы, поэтому флажок придётся поставить на место.
Существует несколько способов проверки утверждений. Первым пунктом в списке — булевая функция. При создании булевой функции, диалог предоставляет вполне подходящий шаблон:
Все записи в библиотеке сохраняются согласно своему типу. Это означает, что, если в дальнейшем понадобится использовать, например, проверку утверждения, необходимо будет помнить не только её название, но и тип. Это очень быстро может оказаться весьма неудобным, особенно в крупных проектах.
При прогоне тестов мы должны увидеть ошибку. Её очень легко исправить:
Теперь можно убедиться, что все тесты проходят с успехом.
При работе с тестами репозиторий блокируется, поэтому по окончании работы необходимо либо закрыть SQL Developer, либо закрыть репозиторий (Deselect Repository).
И, напоследок, проверим запись транзакции. Для этого выберем следующий тип валидации — сравнение результатов запросов (Compare Query Results). Как и следует из названия, он работает очень просто: нужно указать два запроса, результаты которых совпадут. Так как точную дату и время узнать невозможно, можно довольствоваться люб значение в пределах 10 секунд:
Добавим нужный функционал в нашу процедуру:
Отладка
После очередного прогона тестов вдруг выясняется, что ошибка никуда не делась. Вы, наверное, уже заметили ошибку в коде выше, однако в реальных условиях ситуации бывают куда более сложными. Так как разницы инструмент на показывает, придётся выяснять причину вручную. К сожалению, отладочный функционал SQL Developer здесь ничем помочь не в состоянии. Это значит, что нам придётся прогнать тест без выполнения сброса. Для этого можно создать ещё один сценарий — отладочный. А точнее два: один — без сброса, но с тем же динамическим запросом, что и в нерабочем тесте — для того чтобы разобраться в чём дело; а второй — без настройки контекста, но со сбросом — для того чтобы убрать за первым.
После запуска первого сценария можно посмотреть содержимое таблицы, и свериться с проверочным запросом. Теперь чётко видно, что проблема заключалась именно в проверочном запросе. Не забыв запустить второй сценарий для очистки данных, поправляем условия теста, и устраиваем повторный прогон. Теперь всё в порядке. Отладочные сценарии можно оставить на будущее, а первый законченный сценарий можно поместить в новый набор тестов.
Второй сценарий
Теперь когда у нас есть сценарий успешной оправки сообщения, мы можем попробовать сценарий неудачной отправки. Например, когда пост-оплатный клиент неактивен:
Необходимо создать новый сценарий. Нам также придётся слегка подправить настройку контекста и динамический запрос, но это уже гораздо проще, чем создавать новые с нуля.
Для настройки контекста копируем PL/SQL шаг «Активный пост-оплатный клиент», в котором заменяем 1 на 0 и публикуем в библиотеке под названием «Неактивный пост-оплатный клиент». Повторяем то же для динамического запроса, назвав новый запрос «Неотправленное сообщение». Для сброса контекста используем существующий шаг.
После прогона тест должен показать ошибку. Её очень легко исправить. Заменяем SELECT 1 INTO V_IS_QUEUED FROM DUAL на SELECT IS_ACTIVE INTO V_IS_QUEUED FROM CLIENTS WHERE > — и всё снова работает.
Затем необходимо проверить, чтобы транзакция не сохранялась. Для этого используем следующий тип проверки — сравнение таблиц (Compare tables). Поначалу может показаться, что сравнивать не с чем, однако, в настройке контекста есть возможность скопировать существующую таблицу во временную. Нам это прекрасно подходит — можно скопировать транзакции во временную таблицу, а после вызова процедуры сравнить результаты. Главное — не забыть эту таблицу удалить при сбросе контекста. Есть два варианта — восстановить, затем удаилить, и просто удалить. Так как восстанавливать нам нечего — выберем второй вариант. Обратите внимание, что как и в случае со сравнением запросов, единственный вариант обратной связи — есть совпадение или нет.
Полюбовавшись на ошибку после прогона тестов, можно подумать над решением. Например, можно обернуть вставку в условие, исользуя свеже-обновлённый V_IS_QUEUED:
Компилируем процедуру, прогоняем тесты — всё работает.
В заключение, нам надо проверить, что очередь сообщений осталась без изменений. И хотя руки чесались сразу же поместить вставку сообщения внутрь условия рядом со вставкой транзакции, это было бы поощрением нарушения дисциплины. Поэтому сначала создадим дополнительную проверку для этого утверждения. Следующий тип проверки — Запрос, не возвращающий записей (Query returning no rows). Так как мы полностью очищаем все данные после каждого теста, достаточно будет указать SELECT * FROM MESSAGE_QUEUE в качестве такого запроса.
Прогон тестов показывает ошибку, которую мы с лёгкостью устраняем, помещая вставку внутрь условия. И на этом заканчивается наш второй сценарий.
Выводы
SQL Developer можно использовать для разработки хранимых процедур методом TDD. Невзирая на многочисленные недостатки, этот пакет предоставляет платформу для разработки хранимых процедур, позволяя разработчикам с лёгкостью и уверенностью менять и расширять функционал существующих процедур.
К сожалению, тестовый репозиторий можно создать только в СУБД Oracle. Кроме того, попытки использовать сторонние СУБД типа PostgreSQL или даже MySQL в качестве базы данных для тестирования, заканчиваются крахом подсистемы теститования. Также выяснилось, что использование SQL Developer в системах непрерывной интеграции вызывает массу проблем, но это уже отдельная история.