Vincent Partington has more than 10 years of enterprise Java experience. He has written and presented on topics as diverse as performance, security, RIA, and persistence technologies. Vincent Partington is the Chief Technical Officer of XebiaLabs where he is responsible for their flagship deployment automation product Deployit. Vincent is a DZone MVB and is not an employee of DZone and has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

JPA Implementation Patterns: Testing

09.21.2009
| 12901 views |
  • submit to reddit

This week I will discuss various approaches to testing JPA code.The first question to ask is: what code do we want to test? Two kinds of objects are involved when we talk about JPA: domain objects and data access objects (DAO's). In theory your domain objects are not tied to JPA (they're POJO's, right?), so you can test their functionality without a JPA provider. Nothing interesting to discuss about that here. But in practice your domain objects will at least be annotated with JPA annotations and might also include some code to manage bidirectional associations (lazily), primary keys, or serialized objects. Now things are becoming more interesting...


(Even though such JPA specific code violates the POJO-ness of the domain objects, it needs to be there to make the domain objects always function the same way. Whether inside or outside of a JPA container. The managing of bidirectional associations and using UUIDs as primary keys are nice examples of this. In any case, this is code you most certainly need to test.)

Of course, we'd also need to test the DAO's, right? An interesting question pops up here: why do we want to test the DAO's? Most of them just delegate to the JPA provider and testing the JPA provider makes no sense unless we are writing "learning tests" (see also Robert Martin's Clean Code) or developing our own JPA provider. But the combination of the DAO's and the JPA specific part of the domain objects is testworthy.

What to test against?

Now that we know what to test, we can decide what to test against. Since we are testing database code, we want our test fixture to include a database. That database can be an embedded in-memory database such as HSQLDB (in memory-only mode) or a "real" database such as MySQL or Oracle. Using an embedded database has the big advantage of being easy to set up; there is no need for everyone running the tests to have a running MySQL or Oracle instance. But if your production code runs against another database, you might not catch all database issues this way. So an integration test against a real database is also needed, but more on that later.

For most tests we need more than just the database. We need to set it up correctly before a test and after the test we need leave it in a usable state for the next test to run. Setting up the schema and filling the database with the right data before running the test are not that hard to do (a.k.a. left as an exercise for the reader ;-) ), but returning the database to a usable state after the test is a more difficult problem. I've found a number of approaches to this problem:

  • The Spring Framework includes the a test framework that uses transactions to manage the state of your test fixture. If you annotate your test to be @Transactional, the SpringJUnit4ClassRunner will start a transaction before each test starts and roll back that transaction at the end of the test to return to a known state. If you are still using JUnit 3.8 you can extend the AbstractTransactionalSpringContextTests base class for the same effect. This might seem nice but in practice I've found this method to be unsatisfactory for a number of reasons:
    1. By default the JPA context is not flushed until the transaction is committed or a query is executed. So unless your test includes a query, any modifications are not actually propagated to the database which can hide problems with invalid mappings and such. You could try and explicitly invoke EntityManager.flush before the end of the test, but then the tests don't represent real scenario's anymore.
    2. Also, saving an entity and then retrieving it in the same session does not uncover those nasty lazy loading issues. You're probably not even hitting the database as the JPA provider will return a reference to the object that you just saved!
    3. Finally, in a test you might like to first store some data in the database, then run the tests, and finally check that the right data was written out to the database. To test this properly you need three separate transactions without the first two transactions being rolled back.
  • If you use an embedded in-memory database that database will be clean when you run the first test and you won't need to worry about leaving it in a good state after all the tests are run. This means you will not have to roll back any transactions and can have multiple transactions within one test. But you might have to do something special between each test. For example, when using the Spring TestContext framework you can use the @DirtiesContext annotation to reinitialize the in-memory database between tests.
  • If you cannot use an in-memory database or re-initializing it after every test is too expensive, you can try and clear all the tables after every test (or before every test). For example, DbUnit can be used to delete all data from your test tables or truncate all test tables. Foreign key contraints may get in the way though, so you will want to temporarily disable referential integrity before performing these operations.

At what scope to test?

The next thing is to decide on the scope of the tests. Will you write small unit tests, larger component tests, or full-scale integration tests? Because of the way JPA works (the leakiness of its abstraction if you will), some problems might only surface in a larger context. While testing the persist method on DAO in isolation is useful if you want to know whether the basics are correct, you will need to test in a larger scope to shake out those lazy loading or transaction handling bugs. To really test your system you will need to combine small unit tests with larger component tests where you wire your service facades with the DAOs under test. You can use an in-memory databases for both tests. And to complete your test coverage you will need an integration test with the database to be used in production using a tool such a Fitnesse. Because we are not specifically testing the JPA code in that case, having unit tests on a smaller scale will help you pinpoint DAO bugs more quickly.

What to assert?

One final thing to tackle is what to assert in the tests. It might be that your domain objects are mapped to an existing schema in which case you want to make sure that the mapping is correct. In this case you would like to use raw JDBC access to the underlying database to assert that the right modifications were made to the right tables. But if the schema is automatically generated from the JPA mapping you probably will not care about the actual schema. You'll want to assert that persisted objects can be correctly retrieved in a new session. Direct access to the underlying schema with JDBC is not necessary and would only make such test code brittle.

I'm pretty sure that I have not covered all test scenarios in this blog because testing database code is a very complicated area. I would love to hear how you test your database code, whether it uses JPA or some other persistency mechanism.

From http://blog.xebia.com

Published at DZone with permission of Vincent Partington, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Thomas Mueller replied on Mon, 2009/09/21 - 1:51am

> embedded in-memory database

The H2 database supports in-memory operation as well. I read that Apache Derby now also supports it, but I guess it's slower then.

> your production code runs against another database, you might not catch all database issues this way.

That's true, however Hibernate and TopLink abstractes many differences 'away'. The H2 database supports compatibility modes.

Mladen Girazovski replied on Mon, 2009/09/21 - 3:05am

IME EclipseLink doesn't work welll with HSQL and if i'm not mistaken it is not a suppeorted DB for EclipseLink.

How do you set your DB into a specified state before the tests? I'm using  DBUnit, this way i also make sure that there is no dependecies between tests, DBUnit also supports checking if the records have been written properly to the DB.

Mladen Girazovski replied on Mon, 2009/09/21 - 3:06am

How do you set your DB into a specified state before the tests? I'm using  DBUnit, this way i also make sure that there is no dependecies between tests, DBUnit also supports checking if the records have been written properly to the DB.

BTW., TopLink is EclipseLink now ;)

Casper Bang replied on Mon, 2009/09/21 - 3:30am

I find testability to be a concern with JPA. I mean, the entities are not truly decoupled from the "I can access everything" so to me it's not truly OO. And as such, I can not refactor code and use patterns the way I like - it simply won't work in practice when I try in a real world complex database.

So we are forced to do only slow integration testing, by the use of a cusom JUnit/dbUnit annotation processor @Load in front of the  tests. So I guess my question to fellow JPA users is, do you use JPA as ONLY an ORM layer and avoid adding any kind of logic to the entities? If so, how do you avoid building this shadow world of attached storage entities/DTO's and pure OO objects?

 

Salman Khan replied on Tue, 2009/09/22 - 3:25pm

This is a TEST Comment

Salman Khan

Salman Khan

http://www.google.com/

Carl-petter Bertell replied on Thu, 2010/02/11 - 12:47pm

I just had a presentation about the subject and made an example project to go along with it.

It uses Spring, JPA/Hibernate, HSQLDB and DbUnit:

http://code.google.com/p/java-persistence-test-example/

Tomasz Krzyżak replied on Mon, 2011/02/14 - 3:07pm

Try using JPA-Unit - tool for testing your JPA code. It
  • managas transaction,
  • creates db connections,
  • entitymanager based on given persistence unit name,
  • fills db with test-data.
By default it requires zero configuration so its very easyto use.

Leo Jacsion replied on Sat, 2012/12/22 - 12:22am in response to: Mladen Girazovski

 Preservance a professional Website Designing  company based in the capital city of India that offers various services for website designing from a static website to CMS driven website to any open source website development to complete e-commerce site to travel portal. 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.