Java – How to use unit tests and TDD to test an app that relies mostly on database CRUD operations

javajpatddunit testing

At work, one of my projects is mostly about taking data passed in from an external client and persisting it in a database. It's a Java enterprise app using JPA and most of our logic revolves around CRUD operations.

The majority of our bugs involve JPA in one way or another.

  • Example 1: If you click the save button twice, JPA might try to insert the same entity into the database a second time, causing a primary key violation.
  • Example 2: You retrieve an entity from the database, edit it and try to update its data. JPA may try to create a new instance instead of updating the old one.

Often the solution is needing to add/remove/change a JPA annotation. Other times it has to do with modifying the DAO logic.

I can't figure out how to get confidence in our code using unit tests and TDD. I'm not sure if it's because unit tests and TDD are a bad fit, or if I'm approaching the problem wrong.

Unit tests seem like a bad fit because I can only discover these problems at runtime and I need to deploy to an app server to reproduce the issues. Usually the database needs to be involved which I consider to be outside the definition of a unit test: These are integration tests.

TDD seems like a bad fit because the deploy + test feedback loop is so slow it makes me very unproductive. The deploy + test feedback loop takes over 3 minutes, and that's just if I run the tests specifically about the code I'm writing. To run all the integration tests takes 30+ minutes.

There is code outside this mold and I always unit test that whenever I can. But the majority of our bugs and the biggest time sinks always involve JPA or the database.


There is another question that is similar, but if I followed the advice I'd be wrapping the most unstable part of my code (the JPA) and testing everything but it. In the context of my question, I'd be in the same bad situation. What's the next step after wrapping the JPA? IMO that question is (perhaps) a step to answer my question, but not an answer to it.

Best Answer

One option is to use an in-memory testing database such as H2; it tends to be about about 10x faster than a standard disk-using database, and with lower startup/teardown times.

Whether it will help does largely depend on whether the JPA issues you are having are general enough that they will still fail on different database. Not much point running tests faster if they miss the bulk of the problems.

But if you can do 10 runs with H2 for every one with the full system, it could pay off.