Основы тестирования с помощью Java фреймворка DBUnit
DBUnit — Java open source фреймворк (расширение JUnit), который приводит базу данных в определенное состояние между вызовами тестов.
Если вам необходимо перед вызовом каждого теста загружать данные в базу данных, а после вызова тестируемой функциональности, которая модифицирует данные в базе данных, проверять правильность изменений — DBUnit будет хорошим выбором для решения подобных задач.
Как использовать DBUnit
Предположим, что нам необходимо протестировать метод, который запускается планировщиком задач в начале нового года на одном из предприятий:
void updateAllSalaries()
Логика данного метода рассчитывает новое значение зарплаты каждого сотрудника предприятия, основываясь на различных данных (к примеру, опыт работы, отзывы коллег, выполнение задач в поставленные сроки), которые запрашиваются из различных таблиц, и изменят зарплату сотрудников, сохраняя новое значение в таблице EMPLOYEE
, которая имеет следующую структуру:
Для хранения данных, используемых тестируемым методом для анализа, в целях вычисления нового значения заработной платы, добавим таблицу EMPLOYEE_PROGRESS
, которая в столбце PROGRESS
будет содержать количество выполненых задач в поставленные сроки в процентном выражении:
Для простоты, тестируемый метод будет действовать следующим образом при вычислении нового значения зарплаты:
Если прогресс больше 50%, тогда Новая зарплата = SALARY + (SALARY * Progress) / 100. Иначе Новая зарплата = SALARY - (SALARY * Progress) / 100.
Один из самых простых способов создать DBUnit-тест — расширить класс org.dbunit.DBTestCase
, который является непрямым наследником класса junit.framework.TestCase
:
package com.company.test; import org.dbunit.DBTestCase; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSet; public class SimpleDBTestCase extends DBTestCase { @Override protected IDataSet getDataSet() throws Exception { return new FlatXmlDataSet(getClass().getResourceAsStream( "/data/SalaryTestDataSet.xml")); } }
Переопределенный метод getDataSet
возвращает объект IDataSet
, который был создан на основании источника, данные которого будут загружены в базу данных перед стартом теста. В данном случае загружаемые данные находятся в XML файле SalaryTestDataSet.xml
и выглядят следующим образом:
<dataset> <employee ID="1" NAME="Sara" SALARY="465.00"/> <employee ID="2" NAME="David" SALARY="789.00"/> <employee ID="3" NAME="Mariya" SALARY="711.00"/> <employee_progress ID="1" EMPLOYEE_ID="1" PROGRESS="90"/> <employee_progress ID="2" EMPLOYEE_ID="2" PROGRESS="95"/> <employee_progress ID="3" EMPLOYEE_ID="3" PROGRESS="48"/> </dataset>
Каждый
Также нам необходимо добавить информацию о соединении с базой данных.
Для простоты я добавляю эту информацию в конструкторе через системные свойства, хотя есть и другие более изящные способы.
public SimpleDBTestCase(String testName) throws Exception { super(testName); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_DRIVER_CLASS, "oracle.jdbc.driver.OracleDriver"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_CONNECTION_URL, "jdbc:oracle:thin:@localhost:1521:XE"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_USERNAME, "login"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_PASSWORD, "password"); System.setProperty(PropertiesBasedJdbcDatabaseTester.DBUNIT_SCHEMA, "DB_SCHEMA"); }
Добавляем метод для тестирования функциональности:
1. public void testUpdateAllSalaries() throws Exception { 2. AccountService accountService = new AccountService(); 3. accountService.updateAllSalaries(); 4. IDataSet databaseDataSet = getConnection().createDataSet(); 5. ITable actualTable = databaseDataSet.getTable("EMPLOYEE"); 6. IDataSet expectedDataSet = new FlatXmlDataSet(getClass().getResourceAsStream("/dataSalaryTestDataSet_Expected.xml")); 7. ITable expectedTable = expectedDataSet.getTable("EMPLOYEE"); 8. ITable filteredActualTable = DefaultColumnFilter.includedColumnsTable(actualTable, expectedTable.getTableMetaData().getColumns()); 9. Assertion.assertEquals(expectedTable, filteredActualTable); 10. }
В строке 2 и 3 мы вызываем функциональность, которую необходимо протестировать.
Как мы помним, необходимые данные для тестирования этой функциональности уже были загружены в базу данных.
В строке 4 мы получаем доступ к базе данных и создаем объект типа IDataSet
, который представляет все таблицы из базы данных. А в строке 5 получаем интересующую нас таблицу.
В строке 6 мы загружаем из проверочного источника данных (в данном случае тоже XML файл) в объект типа
IDataSetданные, которые, как мы ожидаем, должны быть в базе данных.
Вот как выглядит файл SalaryTestDataSet_Expected.xml
:
<dataset> <EMPLOYEE ID="1" SALARY="883.5"/> <EMPLOYEE ID="2" SALARY="1538.55"/> <EMPLOYEE ID="3" SALARY="369.72"/> </dataset>
Обратите внимание, что здесь мы задаем только те данные, которые мы хотим проверить.
В строке 7 мы получаем таблицу
EMPLOYEEиз проверочного источника данных, хотя эта таблица единственная в этом файле, но мы можем определять и другие таблицы.
В строке 8 мы фильтруем столбцы таблицы, полученной из базы данных (стока 5), и выбираем только те столбцы, которые мы хотим проверить, т.е. те, которые мы определили в проверочном источнике данных, в данном случае в файле /data/SalaryTestDataSet_Expected.xml
.
В строке 9 мы проверяем ожидаемые данные против данных, полученных из базы данных.
Если данные совпадают — тест успешно пройден, иначе — терпит неудачу.
Преимущества DBUnit
Чтобы протестировать метод updateAllSalaries
, к примеру, с помощью JUnit необходимо писать код, который перед вызовом теста создаст и сохранит новую запись о сотруднике в таблице EMPLOYEE
, а также заполнит друге таблицы необходимыми данными для анализа и изменения зарплаты. После того как тестируемый метод закончит работу, необходимо написать код, который проверит правильность нового значения в поле SALARY
таблицы EMPLOYEE
для каждого сотрудника. Данный подход становится все более не приемлемым с ростом сложности тестируемой функциональности, т.к. вам нужно писать все больше и больше кода, чтобы проверить все ветви алгоритма, выполняемого тестируемым методом.
Дополнительные свойства
— Помимо XML файлов DBUnit также поддерживает следующие источники данных: CVS файлы, Excel таблицы, базы данных.
— По умолчанию DBUnit удалит все данные из таблиц, в которые буду загружены
данные для теста, а после окончания теста измененные данные останутся в таблицах. Данное поведение можно изменить, переопределив методы:
@Override protected DatabaseOperation getSetUpOperation() throws Exception { return DatabaseOperation.CLEAN_INSERT; } @Override protected DatabaseOperation getTearDownOperation() throws Exception { return DatabaseOperation.NONE; }
и используя другие значения класса DatabaseOperation
: UPDATE
, INSERT
, REFRESH
, DELETE
, DELETE_ALL
, TRUNCATE_TABLE
.
Также DBUnit обладает возможностью генерирования
Ресурсы
Более подробную информацию вы найдете на сайте www.dbunit.org.Статья о том, как трансформировать XML файлы Hibernate в плоские XML файлы DBUnit.
6 коментарів
Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.