Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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

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

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.

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.

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.

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.

Hibernate has a neat little feature that is heavily under-documented and unknown. You can execute an SQL script during the SessionFactory creation right after the database schema generation to import data in a fresh database. You just need to add a file named import.sql in your classpath root and set either create or create-drop as your hibernate.hbm2ddl.auto property.

http://in.relation.to/Bloggers/RotterdamJBugAndHibernatesImportsql

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top