Интеграционное тестирование — Hibernate и DbUnit

Я пишу некоторые интеграционные тесты в JUnit. Здесь происходит то, что когда я запускаю все тесты вместе подряд (а не по отдельности), данные, сохраняемые в базе данных, всегда изменяются, и тесты обнаруживают неожиданные данные (вставленные предыдущим тестом) во время их выполнения.

Я думал использовать DbUnit, но мне интересно, сбрасывает ли он индекс автоинкремента между каждым выполнением или нет (потому что тесты также проверяют идентификаторы сохраняемых сущностей).

Спасибо

M.


person Mark    schedule 03.05.2010    source источник


Ответы (4)


Рекомендуется перевести базу данных в известное состояние перед выполнением теста, и DBUnit предоставляет для этого все необходимое. Но не полагайтесь на автоматически увеличивающиеся столбцы, поместите их также в свой набор данных DBUnit. Плюсы: вы можете вручную проверить состояние базы данных после выполнения теста, который не прошел. Минусы: вам нужно настроить и поддерживать наборы данных.

Другой подход состоит в том, чтобы запускать каждый тестовый метод внутри транзакции (и откатывать транзакцию в конце выполнения). Плюсы: данные легче настраивать и поддерживать (в базе данных). Минусы: исправлять неудачный тест менее удобно.

person Pascal Thivent    schedule 03.05.2010

Вы можете запускать отдельные тесты или, если применимо, группы тестов как одну транзакцию, а затем выполнять откат в конце. (Это может быть сложно, если сами тесты содержат несколько транзакций, а ваша БД не поддерживает вложенные транзакции или точки сохранения.)

В качестве альтернативы создайте тестовую базу данных с помощью скриптов. Здесь может помочь DbUnit, а также другие генераторы баз данных, такие как LiquiBase, dbmaintain, dbmigrate Затем вы можете удалить всю базу данных и создать ее заново для каждого теста или группы тестов. Полезность этого подхода уменьшается по мере того, как набор тестовых данных становится большим, а накладные расходы возрастают.

Последний вариант — не заставлять ваши тесты зависеть от сгенерированного идентификатора, поскольку в зависимости от сгенерированного значения будут создаваться хрупкие тесты. Полезно проверить сгенерированный идентификатор, поэтому проверьте эти значения для некоторых тестов, но я не уверен, что есть смысл тестировать идентификаторы для всех тестов.

РЕДАКТИРОВАТЬ: ОП спросил об использовании спящего режима для воссоздания схемы. Это можно сделать, создав новый SessionFactory для каждого теста и установив для параметра «hibernate.hbm2ddl.auto» значение «true» при создании SessionFactory. Я упоминаю о снижении эффективности drop-create — это относится и к этому случаю.

person mdma    schedule 03.05.2010
comment
Как насчет того, чтобы заставить Hibernate вручную сбросить схему? Возможно, в методе @After моего базового тестового класса. - person Mark; 03.05.2010
comment
Конечно, если ваша схема в настоящее время уже управляется спящим режимом, то это возможно. Я добавил немного больше к моему ответу. - person mdma; 04.05.2010
comment
Обратите внимание, что, хотя это и возможно, воссоздание схемы для каждого теста вообще не масштабируется и выполняется очень медленно. Это не очень хорошая идея, ИМО. - person Pascal Thivent; 04.05.2010
comment
@Pascal Thivent, согласен - цикл drop-create для каждого теста в целом не очень хорошая идея, будь то создание схемы гибернации или сценарии настройки. - person mdma; 04.05.2010
comment
Это правильно, я тоже чувствую, что это не правильное решение, но как временное решение оно должно работать (он же дедлайн :) - person Mark; 04.05.2010
comment
В любом случае происходит что-то странное, я создал следующий метод в своем базовом тестовом классе (в следующем комментарии из-за ограничения на количество символов). Но, к сожалению, он не работает и неожиданно блокируется после второй операции создания. После многих попыток до сих пор не могу понять, почему: - person Mark; 04.05.2010
comment
@Before public void before() { Конфигурация конфигурации = новая конфигурация().configure(); SessionFactory sessionFactory =configuration.buildSessionFactory(); Сеанс сеанса = sessionFactory.openSession(); Соединение соединение = session.connection(); SchemaExport schemaExport = новый SchemaExport (конфигурация, соединение); schemaExport.execute (ложь, правда, правда, правда); сеанс.закрыть(); } - person Mark; 04.05.2010
comment
Возможно, вы захотите включить метод @After, который удаляет предыдущую SessionFactory. Может быть, есть объекты БД, которые заблокированы из предыдущего теста? Можете ли вы распечатать трассировку стека, чтобы увидеть, где он блокируется? - person mdma; 04.05.2010
comment
К сожалению, я сильно изменил свой код, поэтому теперь сложнее воспроизвести среду. В любом случае проблема, похоже, заключается в управлении сеансом Hibernate между различными выполнениями теста. Базовый класс получает объект сеанса для экспорта схемы, вызывая HibernateUtil.getCurrentSession(), затем тестовый класс выполняет некоторые методы dao, которые внутренне получают объект сеанса таким же образом. Когда выполняется второй тест, HibernateUtil.getCurrentSession() возвращает недопустимый сеанс, и Hibernate генерирует всевозможные исключения. Вот трассировка стека исключения: - person Mark; 07.05.2010
comment
java.lang.StackOverflowError в sun.reflect.GeneratedMethodAccessor24.invoke(неизвестный источник) в sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) в java.lang.reflect.Method.invoke(Method.java:597) в org.hibernate.jdbc.BorrowedConnectionProxy.invoke(BorrowedConnectionProxy.java:74) в $Proxy4.createStatement(неизвестный источник) - person Mark; 07.05.2010

Плохо полагаться на значения идентификатора в тесте, потому что автоинкремент зависит только от базы данных. Поэтому я бы никогда не стал проверять идентификаторы, потому что, если вы это сделаете, ваш тест зависит от того, заполнены ли объекты определенными значениями идентификаторов, что не является примером из реальной жизни. Тест не должен зависеть от идентификаторов автоинкремента.

person khmarbaise    schedule 03.05.2010

По существу этот вопрос может быть решен двумя путями.

  1. Оберните тесты, связанные с БД, в транзакцию. Перед проверкой начните транзакцию. После завершения тестового прогона прервать транзакцию всегда прерывать транзакцию. Таким образом, никакие изменения, сделанные в тесте, не будут сохранены.

  2. Используйте что-то вроде DBUnit и т. Д., Чтобы имитировать классы, связанные с операциями БД, чтобы никакие данные никогда не поступали в БД, а ваши классы возвращали результаты, как если бы операция БД была выполнена.

Если вы получаете доступ к БД при выполнении тестов, я предпочитаю подход 1.

person Fazal    schedule 03.05.2010