В преддверии старта курса "Java QA Automation Engineer" подготовили перевод полезного материала.Также приглашаем поучаствовать в открытом вебинаре на тему «HTTP. Postman, Newman, Fiddler (Charles), curl, SOAP. SoapUI». На этом занятии участники вместе с экспертом разберут, какие бываю API и каким способом можно проверить, что backend отдает ожидаемые данные, а также познакомятся с основными инструментами для тестирования.   Бывает, и достаточно часто, что во время автоматизированного тестирования наши тесты должны взаимодействовать с базами данных. Иногда нам нужно установить какие-либо тестовые данные. В других случаях нам нужно совершать запросы в базу данных, чтобы получить те самые тестовые данные. И давайте не будем забывать об очистке данных, которые мы использовали и которые больше нам не нужны. В этой статье я покажу, как вы можете использовать класс Spring JdbcTemplate для упрощения работы с базой данных MySQL из ваших автоматизированных тестов на Java. Вам также следует взглянуть на это замечательное дополнение к базе данных MySQL, созданное сообществом для библиотеки дополнений TestProject, позволяющее расширить возможности тестирования с помощью предварительно созданных автоматизированных экшенов, которые вы можете мгновенно внедрить в ваши написанные и закодированные тесты. Прежде чем мы сможем начать наши взаимодействия с базой данных, нам нужно кое-что настроить. А именно зависимости, которые нам нужно добавить в наш проект. В моем случае, поскольку я использую Maven, зависимости, которые мне нужно добавить в файл pom.xml, будут выглядеть следующим образом: Первая зависимость, которую мы здесь видим, — это зависимость из пакета Spring. Здесь мы можем найти класс JdbcTemplate, который мы будем использовать для коммуникации с базой данных. Этот класс содержит полезные методы для обновления или получения данных из базы данных. Вторая зависимость требуется для связи с инстансом MySQL. Примечание: эти зависимости имеют последнюю доступную на момент написания статьи версию (какую вы можете увидеть в репозитории Maven). Версия mysql-connector-java должна быть синхронизирована с версией инстанса MySQL, на котором работает ваша база данных. В моем случае, мой сервер MySQL имеет версию > 8, поэтому версия моего «mysql-connector-java» также выше чем 8. После того как мы разобрались с зависимостями, мы можем установить связь с нашей базой данных. Мы могли бы написать код, необходимый для этой операции, в нашем тесте. Однако нам обязательно понадобится этот код и в других тестовых классах. Следовательно, этот код может быть написан либо в специальном классе для работы с базой данных, либо в базовом классе, расширяемом вашими тестами. Независимо от того, какой вариант вы выберете, соединение может быть установлено с помощью подобного метода: Первое, на что нам нужно обратить внимание, это то, что этот метод возвращает DataSource. Это необходимо для инициализации класса JdbcTemplate, который мы будем использовать в наших тестах, поскольку он хранит соединение с базой данных. Затем в качестве имени класса драйвера в этом примере я использовал значение «com.mysql.cj.jdbc.Driver». Опять же это требуется для установки соединения, и в некоторых случаях в более старых версиях зависимостей коннектора MySQL вместо него следует использовать «com.mysql.jdbc.Driver». Если вы используете неправильное имя, вы получите соответствующее предупреждение при попытке подключения к базе данных. Вам нужно будет указать расположение базы данных в методе setUrl. Он состоит из URL-адреса, порта и имени базы данных. И, конечно же, вам необходимо указать имя пользователя и пароль для подключения к базе данных, с помощью методов setUsername и setPassword. Теперь, когда соединение установлено, нам нужно инициализировать класс JdbcTemplate. Мы можем объявить переменную этого типа в нашем тестовом классе: Затем в методе @BeforeAll мы можем инициализировать эту переменную, предоставив соединение, которое мы установили с базой данных: На этом настройка завершена, соединение установлено, и мы можем начать обновление (updating) или запрашивание (querying) базы данных. В классе JdbcTemplate мы можем найти много полезных методов. Один из них, ‘update’, может быть использован для создания и обновления таблиц, добавления в них данных или даже удаления данных. Существует несколько вариантов этого метода (с разными сигнатурами), но тот, который я приведу здесь в качестве примера, принимает один параметр: SQL-запрос в виде String. Пример Создадим две новые таблицы: одну с именем ‘meal’ (блюдо) и ‘ingredient’ (ингредиент). В таблице ‘meal’ мы хотим хранить название блюда, присвоенную ему категорию (представляющую, будь то завтрак, обед или ужин) и автоматически сгенерированный id в качестве первичного ключа (primary key). Для создания таблицы напишем в тестовом методе следующий код: Когда мы запустим тест, таблица будет создана. Для создания таблицы больше ничего не требуется. Допустим, мы также хотим добавить к этому столу два блюда: фахитас из курицы и энчилада. Сделать это легко — просто передадим требуемый SQL-запрос в метод update следующим образом: Как видите, у нас по одному вызову метода update на одну SQL-операцию. Теперь давайте создадим таблицу под названием ingredient. У нее не будет автоматически сгенерированного первичного ключа. Однако у нее будет внешний ключ (foreign key), соответствующий значению meal_id из таблицы meal. Каждая запись в этой таблице представляет собой ингредиент, соответствующий блюду из таблицы meal. Этот внешний ключ свяжет ингредиент с блюдом. Кроме того, в таблице ingredient есть столбцы для хранения названия ингредиента (name), количества (quantity) и единицы измерения (‘uom’ - unit of measure) для количества ингредиента. Для того чтобы создать эту таблицу, а затем добавить к ней внешний ключ, мы снова будем использовать метод update, которому мы передадим соответствующий SQL-запрос: Чтобы иметь больше данных для наших следующих примеров, я также добавлю некоторые данные в таблицу ingredient: Отлично, у нас есть 2 таблицы с данными, которые мы можем запрашивать. Теперь же мы будем использовать разные методы из класса JdbcTemplate для получения результатов разных типов. Если нам нужно запросить из базы данных одно значение, мы можем использовать метод queryForObject. У этого метода также есть несколько вариантов использования, но здесь мы рассмотрим наиболее простой: При вызове этого метода нам нужно указать, какой тип возвращаемого значения должен иметь запрос (Class). Мы могли бы, например, получить значение String (указав String.class) или целое число (указав Integer.class). Пример Нам нужно запросить базу данных, чтобы получить значение meal_id из таблицы meal для блюда ‘Chicken Fajita’. Нам нужно сохранить этот результат в переменной с типом int: Здесь вы можете видеть, что тип возвращаемого значения запроса указан как Integer.class, поэтому результат сохраняется в переменной с типом int. Допустим, в тесте мы также хотим вывести в консоль результат этого запроса: Результатом этого вывода будет: Теперь предположим, что вы хотите получить целую строку из таблицы. Или части строки. Вы можете сделать это с помощью метода queryForMap, которому вы передаете необходимый SQL-запрос: Результат этого запроса можно сохранить в переменной типа Map. Ключи map будут соответствовать имени каждого столбца, которому принадлежит элемент строки. Значение будет соответствовать фактическому значению из строки, соответствующей этому столбцу. Пример Мы хотим извлечь все данные о блюде с id 1 из таблицы ‘meal’, сохранить их в переменной и вывести результат в консоль. Это легко можно сделать следующим образом: Как видите, переменная entireRowAsMap представляет Map, ключи которой — String, а значения — Object. Это происходит потому, что некоторые значения являются целыми числами, некоторые — строками и, конечно же, все эти типы являются объектами в Java. Вывод в консоль для приведенного выше кода: Когда вам нужно получить либо все значения, либо часть значений из конкретного столбца, вы можете использовать метод queryForList. В этом варианте использования я покажу на примере, что для результирующих элементов требуется SQL-запрос и тип возвращаемого значения. Речь идет о типе элементов, которые вы будете сохранять в список (List) Java. Например, если все элементы, которые вы извлекаете с помощью этого запроса, являются целыми числами, типом возврата будет Integer.class. Основной пример использования метода выглядит так: Пример Мы хотим сохранить в список Java все названия ингредиентов, которые есть в таблице ‘ingredient’. Мы также хотим вывести эти значения в консоль. Этого можно добиться следующим образом: Поскольку все названия ингредиентов имеют тип String, тип возвращаемого значения для метода queryForList — String.class. Вот что будет выведено на консоль: Другой вариант использования метода queryForList — получение сразу нескольких строк. В этом случае единственный параметр, требуемый при вызове этого метода, — это SQL-запрос, который собирает данные. Типом возврата будет список элементов типа map, где каждая map будет иметь ключ с типом String и соответствующее значение с типом Object. Этот метод выглядит так: Пример Выберите все значения из таблицы ‘meal’, сохраните и выведите их в консоль. Вывод здесь представляет собой список элементов типа map: В некоторых случаях SQL-запросы нуждаются в передаче параметра для замены захардкоженного значения из запроса. Например, вы можете захотеть выполнить тот же запрос для поиска строки в базе данных на основе ее id. Но вам может потребоваться передать id в тест через DataProvider. Следовательно, при каждом запуске метода для выполнения запроса у вас будет другое значение id. В этом случае для любого запроса, который вы хотите выполнить, вместо указания явного значения вы проставляете символ ?. Это нужно сделать внутри SQL-запроса. Пример В тестовом методе нам нужно выяснить, сколько строк существует в таблице ingredient с именем, которое предоставляется в виде параметра. Результат этого запроса будет сохранен в переменной типа int и будет выведен в консоль. Этого можно добиться следующим образом: Второй параметр, переданный методу queryForObject, — это тип возвращаемого значения для запроса, а третий параметр — это имя параметра, который будет отправлен в запрос из DataProvider. Например, если значение параметра ingredientToLookFor будет ‘chicken’, вывод в консоль будет следующим: Помните мою статью об использовании объектов Java для моделирования данных, извлеченных из БД? Вы можете легко использовать JdbcTemplate для запроса базы данных и извлечения результата непосредственно в объект (Object). Все, что вам нужно для выполнения этой задачи, — это объект Java для моделирования данных; класс преобразователя строк (row mapper), который сопоставляет столбец из базы данных со свойствами объекта; запрос, который извлекает данные в объект с помощью преобразователя строк. Пример Допустим, нам нужно смоделировать данные, соответствующие ингредиенту, название которого содержит текст  ‘yellow’, в объект ингредиента (Ingredient Object). Это означает, что мы хотим, чтобы объект имел те же свойства, что и ингредиент в таблице. Мы хотим сопоставить каждый столбец со свойством. Поэтому мы создадим объект Java под названием Ingredient. Его свойства будут следующими: Рекомендуется синхронизировать имена свойств с именами столбцов базы данных. Таким образом, вы можете легко идентифицировать каждое свойство. Поскольку это объект, нам потребуется создать методы equals, hashCode и toString. Пока я пропущу эту часть. Вместо этого я покажу кое-что еще, что вам понадобиться, а именно сеттеры для каждого свойства. Вы можете легко автоматически сгенерировать их в IntelliJ, используя в редакторе сочетание клавиш Alt+Insert. Они будут выглядеть следующим образом: Вы будете использовать их для отображения данных БД в свойства объекта. И это произойдет внутри класса преобразователя строк, который мы создадим следующим. Тело этого класса выглядит следующим образом: Как видите, этот класс должен реализовать интерфейс под названием RowMapper. Из-за этого нам потребуется реализовать метод mapRow. И внутри этого метода вы будете сопоставлять каждое свойство объекта со столбцом базы данных, используя сеттеры. Так, например, для свойства quantity метод setQuantity установит значение, извлеченное из строки, имя соответствующего столбца которой тоже quantity. Создав класс IntegerRowMapper, мы можем выполнить поставленную задачу, используя queryForObject для извлечения данных, соответствующих желтому (‘yellow’) ингредиенту: Результат этого запроса будет отображаться как объект с соответствующими свойствами: Мы рассмотрели несколько способов работы с базами данных из наших автоматизированных тестов: нужно ли нам обновлять их или просто извлекать данные — в классе JdbcTemplate есть множество методов, которые могут нам помочь с этим. Вы можете выбрать, какой из них использовать, в зависимости от того, что должен возвращать ваш SQL-запрос. Узнать подробнее о курсе "Java QA Automation Engineer". Смотреть открытый вебинар на тему «HTTP. Postman, Newman, Fiddler (Charles), curl, SOAP. SoapUI». Редактор