JPA — как обрезать таблицы между модульными тестами

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

Я использую JUnit 4, JPA 2.0 (Eclipselink) и базу данных Derby в памяти. Есть идеи?

Спасибо, Тео


person Theo    schedule 13.10.2010    source источник
comment
Зачем нужно очищать базу данных после каждого теста?   -  person Pascal Thivent    schedule 13.10.2010
comment
Во избежание побочных эффектов. Я хочу, чтобы каждый тест выполнялся изолированно друг от друга.   -  person Theo    schedule 13.10.2010


Ответы (8)


Просто: перед каждым тестом запускайте новую транзакцию и после теста откатывайте ее. Это даст вам ту же базу данных, что и раньше.

Убедитесь, что тесты не создают новых транзакций; вместо этого повторно используйте существующий.

person Aaron Digulla    schedule 20.07.2011
comment
Да, но таким образом вы не сможете зафиксировать какие-либо ошибки, возникающие во время фиксации транзакции (например, если вы проверяете отношения и т. д.). - person Theo; 20.07.2011
comment
Commit просто закрывает транзакцию и делает данные в БД постоянными. Если у вас есть ошибки FK, вы получите их при очистке сеанса. Нет необходимости в фиксации. - person Aaron Digulla; 21.07.2011
comment
Хорошо, это означает, что сброс имеет тот же эффект, что и фиксация, за исключением того, что транзакции закрываются, а данные становятся постоянными в БД. Попробую. Спасибо - person Theo; 07.08.2011
comment
Чтобы было понятнее: flush() просто создает операторы SQL, ожидающие обработки в памяти. Это позволяет вам вносить много изменений в вашу модель в памяти, а затем сохранять все изменения сразу. Представьте, что вы меняете имя и номер телефона пользователя, а затем удаляете запись. Без такого кеша это может быть три SQL. С кешем это как раз одно. - person Aaron Digulla; 08.08.2011

Самый простой способ сделать это, вероятно, использовать метод jpa nativeQuery.

@After
public void cleanup() {
    EntityManager em = entityManagerFactory.createEntityManager();
    em.getTransaction().begin();
    em.createNativeQuery("truncate table person").executeUpdate();
    em.createNativeQuery("truncate table preferences").executeUpdate();
    em.getTransaction().commit();
}
person Chris Hinshaw    schedule 22.04.2013
comment
'truncate' - это оператор DDL, и нет необходимости начинать транзакцию, все DDL автоматически фиксируются. - person gadon; 31.12.2013
comment
Я бы согласился, но для createNativeQuery требуется транзакция. В противном случае это вызывало исключение. По крайней мере, так было в спящем режиме. - person Chris Hinshaw; 31.12.2013
comment
@gadon: это зависит от базы данных. По крайней мере, PostgreSQL, DB2 и Informix имеют (некоторую) поддержку транзакционного DDL. Однако Oracle этого не делает. - person sleske; 20.01.2016

Я немного сбит с толку, так как DBUnit повторно инициализирует базу данных до известного состояния перед каждым тестом.

Они также рекомендуют в качестве передовой практики не очищать или иным образом изменять данные после теста.

Так что, если вам нужна очистка, чтобы подготовить базу данных к следующему тесту, я бы не стал беспокоиться.

person Peter Tillemans    schedule 13.10.2010
comment
На самом деле, я не использую DBUnit. Я только что попробовал упомянутую операцию DELETE_ALL. Я заполняю свою базу данных методом JUnit @Before. Подходит ли DBUnit для JPA? - person Theo; 13.10.2010
comment
Я использую Unitils, который работает с dbunit. Вы создаете набор данных с тем, что вам нужно (в xml: никто не идеален), добавляете пару аннотаций в свой тестовый класс, и все готово. См.: unitils.org/tutorial.html#Testing_with_JPA. - person Peter Tillemans; 13.10.2010

Да, проверка в транзакции значительно облегчит вам жизнь, но если вам нужна транзакция, вам необходимо реализовать компенсирующую транзакцию (транзакции) во время очистки (в @After). Это звучит трудоемко, и может быть, но при правильном подходе вы можете получить набор вспомогательных методов (в тестах), которые компенсируют (очищают) данные, накопленные во время @Before и тестов (с использованием JPA или прямого JDBC - что имеет смысл).

Например, если вы используете JPA и вызываете методы создания сущностей во время тестов, вы можете использовать (используя АОП, если хотите, или просто вспомогательные методы тестирования, такие как мы) шаблон во всех тестах, чтобы:

  1. отслеживать идентификаторы всех объектов, которые были созданы во время теста
  2. накапливать их в порядке создания
  3. объекты воспроизведения удаляются для этих объектов в обратном порядке в @After
person topchef    schedule 13.10.2010
comment
Этот подход звучит хорошо, но, на мой вкус, все еще слишком много работы. Даже если вы отслеживаете созданные объекты, все может пойти не так, если вы удалите их в неправильном порядке (из-за проблем с ссылочной целостностью). - person Theo; 21.12.2010
comment
Да, это слишком сложно для небольших наборов тестов. Чтобы предотвратить проблемы с зарезервированными объектами, объекты удаляются в порядке, обратном их созданию. Основное преимущество заключается в том, что после того, как это будет сделано, очистка теста станет прозрачной и не потребует от разработчиков дополнительных усилий. Чем больше тестов, тем больше смысла. Еще одно преимущество: все разработчики используют один и тот же фреймворк для создания тестовых данных — иначе их тесты либо не будут работать, либо что-то сломают. - person topchef; 21.12.2010

Моя установка очень похожа: это Derby (встроенный) + OpenJPA 1.2.2 + DBUnit. Вот как я обрабатываю интеграционные тесты для моей текущей задачи: в каждом @Before методе я запускаю 3 скрипта:

  1. Drop DB скрипт SQL, который удаляет все таблицы.
  2. Создайте БД сценарий SQL, который воссоздает их.
  3. XML-скрипт модуля БД для конкретного теста для заполнения данных.

В моей базе данных всего 12 таблиц, а набор тестовых данных не очень большой, около 50 записей. Каждый скрипт выполняется около 500 мс, и я поддерживаю их вручную при добавлении или изменении таблиц.

Этот подход, вероятно, не рекомендуется для тестирования больших баз данных и, возможно, даже не может считаться хорошей практикой для небольших; однако у него есть одно важное преимущество перед откатом транзакции в методе @After: вы можете определить, что происходит при фиксации (например, сохраняющиеся отсоединенные объекты или исключения оптимистичной блокировки).

person MaDa    schedule 20.07.2011

Лучше поздно, чем никогда... У меня была такая же проблема, и я нашел довольно простое решение:

  1. установите для свойства "...database.action" значение "drop-and-create" в конфигурации вашего сохраняемого модуля.
  2. закрывать менеджер сущностей и фабрику менеджеров сущностей после каждого теста

постоянство.xml

    <persistence-unit name="Mapping4" transaction-type="RESOURCE_LOCAL" >
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>...</class>
    <class>...</class>

    <properties>
        ...
        <property name="javax.persistence.schema-generation.database.action" value="drop-and-create" />
        ...
    </properties>
</persistence-unit>

модульный тест:

...
@Before
public void setup() {
    factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
    entityManager = factory.createEntityManager();
}


@After
public void tearDown() {
    entityManager.clear();
    entityManager.close();
    factory.close();
}

...

person Michael Marton    schedule 12.06.2018

Я удаляю файл БД после каждого запуска:

boolean deleted = Files.deleteIfExists(Paths.get("pathToDbFile"));

Немного грязный, но работает для меня. С уважением

person pablozoani    schedule 06.04.2020

Вариант 1. Вы можете отключить проверки внешнего ключа перед усечением таблиц и снова включить их после усечения. Таким образом, у вас все еще будут проверки в тестах.

Вариант 2. База данных H2 уничтожает базу данных в памяти при закрытии последнего соединения. Я думаю, что Derby DB поддерживает что-то подобное, или вы можете переключиться на H2.

См. также: я написал код для усечения таблиц перед каждым тестом с использованием Hibernate в связанном вопросе: https://stackoverflow.com/a/63747005/471214

person mmdemirbas    schedule 04.09.2020