Java Reference
In-Depth Information
essence,
JPA
testing isn't much different than testing regular database access code, and
hence most of the techniques explained in chapter 17 apply here. A few differences
and caveats are worth mentioning though, and we cover them in the next subsections.
But first, let's consider some aspects of
JPA
testing.
W
HAT
SHOULD
BE
TESTED
?
JPA
programming could be divided in two parts: entities mapping and
API
calls. Ini-
tially, you need to define how your objects will be mapped to the database tables, typi-
cally through the use of Java annotations. Then you use an
EntityManager
object to
send these objects to or from the database: you can create objects, delete them, fetch
them using
JPA
queries, and so on.
Consequently, it's a good practice to test these two aspects separately. For each per-
sistent object, you write a few test cases that verify that they're correctly mapped (there
are many caveats on
JPA
mapping, particularly when dealing with collections). Then
you write separate unit tests for the persistence code itself (such as
DAO
objects). We
present practical examples for both tests in the next subsections.
T
HE
EMBEDDED
DATABASE
ADVANTAGE
As we mentioned in section 17.1.3, unit tests must be fast to run, and database access
is typically slow. A way to improve the access time is to use an in-memory embedded
database, but the drawback is that this database might not be totally compatible with
the database the application uses.
But when you use
JPA
, database compatibility isn't an issue—quite the opposite.
The
JPA
vendor is responsible for
SQL
code generation,
5
and vendors typically sup-
port all but the rarest databases. It's perfectly fine to use a fast embedded database
(like
HSQLDB
or its successor,
H2
) for unit tests. Better yet, the project should be set
in such a way that the embedded database is used by default, but databases could be
easily switched. That would take advantage of the best of both worlds: developers
would use the fast mode, whereas official builds (like nightly and release builds)
would switch to the production database (guaranteeing the application works in the
real scenario).
C
OMMITMENT
LEVEL
JPA
operations should happen inside a transaction, which typically also translates to a
vendor-specific session. A lot of
JPA
features and performance depends on the transac-
tion/session lifecycle management: objects are cached, new
SQL
commands are issued
on demand to fetch lazy relationships, update commands are flushed to the database,
and so on.
On the other hand, committing a transaction is not only an expensive operation,
but it also makes the database changes permanent. Because of that, there's a tendency
5
This is the ideal scenario; some applications might still need to manually issue a few SQL commands because
of JPA bugs or performance requirements. These cases are rare, though, and they could be handled separately
in the test cases.