Начало: DbUnit: общее описание
Предположим, нам необходимо реализовать тест приложения, сохраняющего данные в БД. Для реализации принципов best practise нам потребуется проинициализировать базу данных (например, очистить её), затем подать на вход приложения какие-то тестовые данные и проверить результат работы приложения, сохранённый в БД. Как это сделать?
1) Создать datasource, например, с помощью Spring в файле "app-ds.xml", подключиться к БД и получить connection для дальнейшей работы с базой:
Параметры initialSize и maxActive определяют начальный и максимальный размер пула создаваемых соединений. В данном случае при создании dbTester будет автоматически сформирован пул из 5 соединений к БД, которые можно получать dbTester.getConnection(). Шестое и последующие соединения будут создаваться непосредственно при необходимости. Если приложение одновременно откроет 10 соединений, то следующий вызов dbTester.getConnection() приведёт к блокировке выполнения программы до тех пор, пока какое-либо из соединений не будет освобождено.
2) Выполнить инициализацию базы данных перед запуском теста. Как уже говорилось чуть раньше, в нашем случае инициализация заключается в очистке. Вопрос в том, какие таблицы очищать? Это можно настроить в отдельном dataset, например, "cleaner.xml":
В java коде инициализация будет выглядеть так:
Для автоматизации вызова этого действия помещаем его в метод с junit-нотацией @Before:
3) Выполнить тест, т.е. подать на вход тестируемого приложения тестовые данные.
4) Проверить результат работы тестируемого приложения - то, какие данные он сохранил в БД. Для этого настроить новый датасет, например, так:
В java коде
Вызов метода checkDatabase удобно поместить под junit-нотацию @After:
В результате выполнения перечисленных шагов будет написан тест, в котором инициализация и проверка состояния БД выполняются с помощью DbUnit, а информация для этого содержится в простых и понятных xml файлах.
Далее, при написании следующих тестов весь java код (реализующий коннект к БД, инициализацию, проверку состояния) останется прежним; не изменятся и datasource/datasets. Соответственно, новые тесты потребуют только написание новых датасетов для пункта 4, и всё!
Предположим, нам необходимо реализовать тест приложения, сохраняющего данные в БД. Для реализации принципов best practise нам потребуется проинициализировать базу данных (например, очистить её), затем подать на вход приложения какие-то тестовые данные и проверить результат работы приложения, сохранённый в БД. Как это сделать?
1) Создать datasource, например, с помощью Spring в файле "app-ds.xml", подключиться к БД и получить connection для дальнейшей работы с базой:
<bean name="iDataSource" class="org.apache.commons.dbcp.BasicDataSource">В java коде
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="url" value="jdbc:sqlserver://localhost:1433;username=user;password=pwd;databaseName=dbase"/>
<property name="maxActive" value="10"/>
<property name="initialSize" value="5"/>
</bean>
import org.apache.commons.dbcp.BasicDataSource;
import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.IDatabaseTester;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
private static IDatabaseTester dbTester = null;
private static IDatabaseConnection dbConnection = null;
private static void setUpClass () {
// load context
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-ds.xml");
// configure dbunit
dbTester = new DataSourceDatabaseTester(ctx.getBean("iDataSource", BasicDataSource.class));
// set start operation (here - cleaning)
dbTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
// set finish operation (here - nothing)
dbTester.setTearDownOperation(DatabaseOperation.NONE);
// try to connect and get a connection
dbConnection = dbTester.getConnection();
}
DbUnit реализует несколько вариантов DatabaseOperation, кроме уже перечисленных это DELETE, UPDATE, INSERT, REFRESH.
Параметры initialSize и maxActive определяют начальный и максимальный размер пула создаваемых соединений. В данном случае при создании dbTester будет автоматически сформирован пул из 5 соединений к БД, которые можно получать dbTester.getConnection(). Шестое и последующие соединения будут создаваться непосредственно при необходимости. Если приложение одновременно откроет 10 соединений, то следующий вызов dbTester.getConnection() приведёт к блокировке выполнения программы до тех пор, пока какое-либо из соединений не будет освобождено.
2) Выполнить инициализацию базы данных перед запуском теста. Как уже говорилось чуть раньше, в нашем случае инициализация заключается в очистке. Вопрос в том, какие таблицы очищать? Это можно настроить в отдельном dataset, например, "cleaner.xml":
<dataset>Каждый тэг - название отдельной таблицы. А значит, в одном датасете можно перечислить любые требуемые таблицы.
<client_message/>
<send_sms/>
<result/>
</dataset>
В java коде инициализация будет выглядеть так:
public void initDababase (IDatabaseTester tester) throws Exception {В результате три таблицы БД - client_message, send_sms и result - будут очищаться dbunit'ом при вызове метода initDababase.
// get resource as stream
InputStream stream = clazz.getResourceAsStream ("cleaner.xml");
// parse and build dataset
IDataSet dataset = new FlatXmlDataSetBuilder().build(source);
// set dataset
tester.setDataSet(dataset);
// call setup operation (here - clean up)
tester.onSetup();
}
Для автоматизации вызова этого действия помещаем его в метод с junit-нотацией @Before:
@Before
public void setUpTest () throws Exception {
initDatabase(...);
}
3) Выполнить тест, т.е. подать на вход тестируемого приложения тестовые данные.
4) Проверить результат работы тестируемого приложения - то, какие данные он сохранил в БД. Для этого настроить новый датасет, например, так:
<dataset>В одном датасете - т.е. в одном файле - настраивается ожидаемое содержимое всех трёх таблиц. Для каждой таблицы заводится отдельный тэг с именем таблицы, а проверяемые поля и их значения указываются как атрибуты этих тэгов.
<client_message message_identifier="send.1"
msisdn="79000000000"
priority="0"
error_id="[NULL]"
status="0" />
<send_sms sms_index="1"
body_encoding="1"
msisdn="79000000000"
priority="0"
status="0" />
<result sms_index="1"
msisdn="79000000000"
status="0" />
</dataset>
В java коде
/**В результате DbUnit прочитает датасет из файла и сформирует ITable expectedTable, затем прочитает БД и сформирует ITable actualTable, а затем сравнит их и в случае расхождений выдаст ошибку, например, такую:
* Checking of database content.
* @param connection connection to database;
* @param source input stream with an expected content;
* @param tables array of table names.
* @throws Throwable any trouble during the work including assertion errors.
*/
public static void checkDatabase (IDatabaseConnection connection, InputStream source, String[] tables) throws Throwable {
IDataSet actualDs = connection.createDataSet();
//build expected dataset through FlatXmlDataSetBuilder and then decorate it through ReplacementDataSet
ReplacementDataSet expectedDs = new ReplacementDataSet(new FlatXmlDataSetBuilder().build(source));
//as the result we can check nulls in easy way
expectedDs.addReplacementObject("[NULL]", null);
for (String tableName : tables) {
ITable expectedTable = getExpectedTable(tableName, expectedDs);
ITable actualTable = getActualTable(tableName, actualDs, expectedTable.getTableMetaData().getColumns());
try {
Assertion.assertEquals(expectedTable, actualTable);
} catch (AssertionError t) {
logger.warn("Assertion failed for table " + tableName + "!");
logger.warn("Expected table content:" + itableToString(expectedTable));
logger.warn("Actual table content:" + itableToString(actualTable));
throw t;
}
}
}
ERROR [DbUnitAssert] junit.framework.ComparisonFailure: row count (table=client_message) expected:<[1]> but was:<[0]>
WARN [DbUnitHelper] Assertion failed for table client_message!
WARN [DbUnitHelper] Expected table content: Row 1: message_identifier="send.1", msisdn="79000000000", priority="0", error_id="null", status="0"
WARN [DbUnitHelper] Actual table content: Empty table!
Вызов метода checkDatabase удобно поместить под junit-нотацию @After:
@After----------------------------------------------------------------------------------------------------
public void tearDownTest () throws Exception {
checkDatabase(...);
}
В результате выполнения перечисленных шагов будет написан тест, в котором инициализация и проверка состояния БД выполняются с помощью DbUnit, а информация для этого содержится в простых и понятных xml файлах.
Далее, при написании следующих тестов весь java код (реализующий коннект к БД, инициализацию, проверку состояния) останется прежним; не изменятся и datasource/datasets. Соответственно, новые тесты потребуют только написание новых датасетов для пункта 4, и всё!
Комментариев нет:
Отправить комментарий