Pergunta

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

Foi útil?

Solução

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.

Outras dicas

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

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

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.

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

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

...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top