Java Reference
In-Depth Information
of concurrent connections), it's a good practice to open it at the beginning of the
tests and close it at the end. Alternatively, if a connection pool was used instead, these
methods could be defined at
@Before
/
@After,
respectively.
At
D
,
connection
is a just a regular
JDBC
connection (
java.sql.Connection
),
but
dbunitConnection
is a DbUnit
IDatabaseConnection
instance. DbUnit uses
IDatabaseConnection
to encapsulate access to the database, and it provides imple-
mentations for the most common databases. The advantage of using a specialized
IDatabaseConnection
implementation (
HsqldbDatabaseConnection
in this case)
rather than the generic one (
DatabaseConnection
) is that it can handle nuances spe-
cific to that database, like conversion between nonstandard
SQL
types and Java classes.
Notice that in this example, both
connection
and
dbunitConnection
were created
using hardcoded values; in real projects, a better practice would be to define these set-
tings externally, such as in a property file. That would allow the same tests to be run in
different environments, such as using alternate databases or getting the connection
from a pooled data source. Getting an
IDataSet
from an
XML
file is so common that
it deserves its proper method
F
. It's also a good practice to load these
XML
files as
resources in the classpath, instead of physical files in the operating system. And if the
file isn't found in the classpath (which is a common scenario when you're writing the
test cases—you might have forgotten to create the file or misspelled its name),
get-
ResourcesAsStream()
returns
null
(instead of throwing a resource-not-found excep-
tion), which in turn would cause a
NullPointerException
in the caller method.
Because an
NPE
is a sure bet to cause a lot of headaches, adding an
assertNotNull()
G
with a meaningful message is a one-liner that can save you time troubleshooting.
Finally,
H
the DbUnit job is effectively performed. We have a dataset (
setupData-
Set
) with the data we want to insert and a connection to the database (
dbunit-
Connection
). All that's left is the class responsible to do the dirty work, and that's
DatabaseOperation
or, more precisely, one of its subclasses. In this case, we use
CLEAN_INSERT
, which first deletes all rows from all tables defined in dataset and then
inserts the new ones. See the next section for more details about
DatabaseOperation
and its implementations.
6
A final note about transactions: in order to keep this example simple, we aren't
dealing with transactions at all, and every database operation is done in one transac-
tion (using
JDBC
's autocommit feature). Although this simplification is fine here, usu-
ally the test cases must be aware of the transaction semantics. For instance, a
transaction could be started before the test (using a
@Before
method) and committed
afterwards (using
@After
), the test cases could explicitly set the autocommit property
(particularly if the connections were obtained from a pool), and so on. The exact
approach depends on many factors, like the type of test (unit or integration) being
written and the underlying technologies used in the code tested (like pure
JDBC
or
ORM
frameworks).
6
6
Chapter 18 offers more detailed insight on transactions in JPA-based test cases.