views:

1532

answers:

9

I have a class I'm unit testing that requires fairly extensive database setup before the individual test methods can run. This setup takes a long time: for reasons hopefully not relevant to the question at hand, I need to populate the DB programatically instead of from an SQL dump.

The issue I have is with the tear-down. How can I easily rollback all the changes made in the db setup phase?

I'm currently using Hibernate + Spring Transactional Testing support, such that my individual test methods are wrapped in transactions.

One solution would be to do the db setup within each test method, such that the db setup would be rolled back automatically. However, the test methods would take forever to run since each method would need to re-prep the database.

Any other ideas? Basically, I'm looking for a way to run my db setup, run my individual tests (each wrapped in a transaction which gets rolled-back after execution), and then roll-back the initial db setup. Any ideas on making this working in a Hibernate / Spring / Junit fashion? Is there a Hibernate "drop all tables" equivalent command?

A: 

You may want to look at @AfterClass annotation, for Junit 4. This annotation will run when the tests are done.

http://cwiki.apache.org/DIRxDEV/junit4-primer.html

James Black
A: 

DNUnit should help you in this regard. You can create separate data sets for each individual test case if you wish.

A: 

DBUnit will help a lot with this. You could theoretically turn off autocommits on JDBC, but it will get hairy. The most obvious solution is to use DBUnit to set your data to a known state before you run the tests. IF for some reason you need your data back after the tests are run, you could look at @AfterClass on a suite that runs all of your tests, but it is generally considered a better practice to set up your tests and then run them, so that if the test fails, it is not just because it didn't have a prestine environment due to a failure to clean up an different test. You ensure that each test sets up its environment directly.

Yishai
wouldn't using DBUnit be conceptually equivalent to SQL scripting that was ruled out in the question?
grigory
I didn't mention it specifically in the question, but I am familiar with DBUnit and I have been avoiding it because I didn't want to have to maintain a ton of DBUnit test fixtures when it was easier to generate the DB programatically. However, I may bite the bullet lacking a better solution.
Brian Ferris
A: 

One solution that you may want to consider is to use a "manual" rollback or compensating transaction in db tear down. I suppose (and if it's not then it should be a trivial add-on to your Hibernate entities) all your entities have datetime create attribute indicating when they were INSERTed into the table. Your db setup method should record time before everything else. Then you have rather simple procedure for db tear down to delete all entities that were created after time recored in db setup.

Of course, this won't work for updates in db setup... But if you have limited number of updates then consider saving pristine image for this type of data and restore it during db tear down.

grigory
+4  A: 

Are you stuck with a specific database vendor? If not, you could use an in-memory database, such as HSQLDB. When you are done with the tests you just throw away the state. This is only appropriate if the tables can be empty at the start of the test suite (before your programmatic setup, that is).

You still need to create tables, but if everything is neatly mapped using Hibernate you can use the hbm2ddl to generate your tables. All you have to do is add the following to your test session factory definition:

<session-factory>
    ...
    <property name="hibernate.hbm2ddl.auto">create</property>
    ...
</session-factory>

If this solution seems applicable I can elaborate on it.

waxwing
Sometimes I forget about the simplest options. Bonus points to you.
Brian Ferris
So by accepting this as the solution does that mean you opted for the db setup/tear down per individual test?
Adam B
I actually went with setting up the db once for a set of tests defined in the same class using a static @BeforeClass Junit method. But it would be easy enough to do on per-test basis with a @Before JUnit method.
Brian Ferris
@Brian - Can you share how you set up a database from within a static method with no access to an application context or getClass()?
HDave
A: 

If you're working with relatively small database, and with a DBMS that can do backups/exports of it relatively fast (like MS SQL Server), you can consider creating a database backup before the tests, and then restore it when all testing is complete. This enables you to set-up a development/testing database and use it as a starting state for all your tests.

I did it with native JDBC, executing ''backup database'' and ''restore database'' T-SQL in-between tests, and it worked reasonably well.

However, this approach is dependent on having the DBMS server on your local machine (for reasonable speed), you having sufficient privileges (which than should not be a problem), and the total size of database not exceeding a few tens on MB - at least in my experience.

javashlook
A: 

Is there a reason that you have to have a connection to the database to run your unit tests? It sounds like it might be easier to refactor your class so that you can mock the interaction with the database. You can mock classes (with some exceptions) as well as interfaces with EasyMock (www.easymock.org).

If your class relies on a complex pre-existing state in a connected database, it would probably be easier to write faster executing tests using mocks. We don't know what the size of your project is or how often your tests are run, but execution time might be something to think about, especially in a large project.

Paul Morie
A: 

@Brian - Can you share how you set up a database from within a static method with no access to an application context or getClass()? – HDave Jun 30 at 22:03

yea, how is this done?

elonderin