Theo
Theo

Reputation: 3125

JPA - How to truncate tables between unit tests

I want to cleanup the database after every test case without rolling back the transaction. I have tried DBUnit's DatabaseOperation.DELETE_ALL, but it does not work if a deletion violates a foreign key constraint. I know that I can disable foreign key checks, but that would also disable the checks for the tests (which I want to prevent).

I'm using JUnit 4, JPA 2.0 (Eclipselink), and Derby's in-memory database. Any ideas?

Thanks, Theo

Upvotes: 16

Views: 26299

Answers (9)

Kani
Kani

Reputation: 840

Not the correct answer to this question. But was the first result from google page so here is the answer for Hibernate users:

import jakarta.persistence.EntityManager;
import org.hibernate.Session;

Session session = entityManager.unwrap(Session.class);
session.getSessionFactory().getSchemaManager().truncateMappedObjects();

Just call it in beforeEach.

Upvotes: 1

mmdemirbas
mmdemirbas

Reputation: 9168

Option 1: You can disable foreign key checks before truncating tables, and enable them again after truncation. You will still have checks in tests in this way.

Option 2: H2 database destroys the in-memory database when the last connection closed. I guess Derby DB supports something similar, or you can switch to H2.

See also: I wrote a code to truncate tables before each test using Hibernate in a related question: https://stackoverflow.com/a/63747005/471214

Upvotes: 0

pablozoani
pablozoani

Reputation: 33

I delete the DB file after each run:

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

A little dirty but works for me. Regards

Upvotes: 0

Michael Marton
Michael Marton

Reputation: 655

Better late then never ... I just had the same problem and came around a pretty simple solution:

  1. set the property "...database.action" to the value "drop-and-create" in your persistence-unit config
  2. close the entity-manager and the entity-manager factory after each test

persistence.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>

unit-test:

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


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

...

Upvotes: 0

Chris Hinshaw
Chris Hinshaw

Reputation: 7285

The simplest way to do this is probably using the nativeQuery jpa method.

@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();
}

Upvotes: 19

MaDa
MaDa

Reputation: 10762

My setup is quite similar: it's Derby (embedded) + OpenJPA 1.2.2 + DBUnit. Here's how I handle integration tests for my current task: in every @Before method I run 3 scripts:

  1. Drop DB — an SQL script that drops all tables.
  2. Create DB — an SQL script that recreates them.
  3. A test-specific DB unit XML script to populate the data.

My database has only 12 tables and the test data set is not very big, either — about 50 records. Each script takes about 500 ms to run and I maintain them manually when tables are added or modified.

This approach is probably not recommended for testing big databases, and perhaps it cannot even be considered good practice for small ones; however, it has one important advantage over rolling back the transaction in the @After method: you can actually detect what happens at commit (like persisting detached entities or optimistic lock exceptions).

Upvotes: 0

Aaron Digulla
Aaron Digulla

Reputation: 328830

Simple: Before each test, start a new transaction and after the test, roll it back. That will give you the same database that you had before.

Make sure the tests don't create new transactions; instead reuse the existing one.

Upvotes: 6

topchef
topchef

Reputation: 19823

Yes, in-transaction test would make your life much easier, but if transaction is your thing then you need to implement compensating transaction(s) during cleanup (in @After). It sounds laborious and it might be but if properly approached you may end up with a set of helper methods (in tests) that compensate (cleanup) data accumulated during @Before and tests (using JPA or straight JDBC - whatever makes sense).

For example, if you use JPA and call create methods on entities during tests you may utilize (using AOP if you fancy or just helper test methods like us) a pattern across all tests to:

  1. track ids of all entities that have been created during test
  2. accumulate them in order created
  3. replay entity deletes for these entities in reverse order in @After

Upvotes: 2

Peter Tillemans
Peter Tillemans

Reputation: 35341

I am a bit confused as DBUnit will reinitialize the database to a known state before every test.

They also recommend as a best practice not to cleanup or otherwise change the data after the test.

So if it is cleanup you're after to prepare the db for the next test, I would not bother.

Upvotes: 2

Related Questions