2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * This package provides test library for database testing in general and JPA testing
19 * specifically. As part of this it provides a means to transparently start an inmemory
20 * database from a junit test or connect to an external database. Also, given a persistence
21 * unit it is easy to start testing it in a junit test with only a few lines of code.
25 * The main use cases are explained below:
28 * <li> <a href="#db-basic">Basic database testing, transparently connecting to a database</a>
30 * <li> <a href="#db-basic-external">Connecting to an external database</a>
32 * <li> <a href="#db-utils">Executing code within a JDBC transaction</a>
34 * <li> <a href="#db-unit">Using DB Unit in your tests</a>
36 * <li> <a href="#db-jpa-basic">Basic JPA testing</a>
38 * <li> <a href="#db-jpa-plus-jdbc">JPA testing combined with JDBC and DBUnit</a>
40 * <li> <a href="#db-jpa-services">Testing a service that requires a transaction</a>
45 * See the <a href="#design-overview">design overview</a> for a an overview of the design.
49 * <h2>Basic database testing, transparently connecting to a database</h2>
51 * Starting the database:
53 * Database db = DatabaseBuilder.getDatabase();
54 * DataSource dataSource = db.start();
56 * If nothing is specified in the user's environment, an inmemory database is started (derby).
57 * Using the datasource is just standard JDBC now.
58 * Stopping the database
63 * <a name="db-basic-external">
64 * <h2>Connecting to an external database</h2>
67 * Connecting to an external database can be done by requiring the 'external' capability on
68 * the database provider.
70 * Database db = DatabaseBuilder.getDatabase(DatabaseProvider.CAPABILITY_EXTERNAL);
72 * This also requires a number of environment variables or system properties to be set,
73 * see {@link ExternalDatabase}.
77 * <h2>Executing code within a JDBC transaction</h2>
80 * To execute code withing a JDBC transaction, use the {@link DatabaseUtils} and
81 * use the {@link DatabaseUtils#executeInTransaction(org.wamblee.support.persistence.DatabaseUtils.JdbcUnitOfWork)}
85 * DatabaseUtils dbutils = new DatabaseUtils(dataSource);
86 * boolean result = dbutils.executeInTransaction(
87 * new JdbcUnitOfWork<Boolean>() {
89 * public Boolean execute(Connection aConnection) throws Exception {
90 * ResultSet res = jpaTester.getDbUtils().executeQuery(
91 * aConnection, GROUP_QUERY, aGroup);
96 * {@link DatabaseUtils} also provides various other utility methods to work with JDBC queries.
99 * <h2>Using DB Unit in your tests</h2>
102 * <p>To work with <a href="http://dbunit.org">DBUnit</a>, {@link DatabaseUtils#createDbTester(org.dbunit.dataset.filter.ITableFilterSimple)}
103 * must be used passing it in the tables to use in the form of a {@link org.dbunit.dataset.filter.ITableFilterSimple}
107 * IDatabaseTester dbtester = dbutils.createDbTester(new ITableFilterSimple() {
108 * public boolean accept(String aTableName) throws DataSetException {
109 * return aTableName.startsWith("XYZ_");
114 * <p>The reason for using a <code>DatabaseUtils</code> instead of DBUnit directly is that
115 * <code>DatabseUtils</code> will keep track of connections and close them when <code>DatabaseUtils</code>
118 * <a name="db-jpa-basic">
119 * <h2>Basic JPA testing</h2>
122 * First step is to create a {@link PersistenceUnitDescription} that matches the persistence unit you
125 * <p>Second step is to make sure that all entities are listed explicitly in your
126 * <pre>persistence.xml</pre>. Currently, class path scanning appears to fail when
128 * Specifying all entities explicitly is not necessarily a bad thing as it is also more efficient.
132 * Now create a <code>JpaTester</code> in your test code:
136 * public void setUp() throws Exception {
138 * // First we create the JpaTester by telling us which persistence unit we
139 * // are going to test
140 * jpaTester = new JpaTester(new MyPersistenceUnit());
146 * Then in test code execute some JPA code within a unit of work:
149 * jpaTester.getJpaBuilder().execute(new JpaUnitOfWork<Void>() {
150 * public Void execute(EntityManager aEm) {
151 * MyEntity entity = new MyEntity("a", "b");
152 * aEm.persist(entity);
158 * <a name="db-jpa-plus-jdbc">
159 * <h2>JPA testing combined with JDBC and DBUnit</h2>
161 * <p>The <code>JPATester</code> provides access to all required object. It is usually convenient to
162 * get them directly from the <code>JPATester</code> after initializing it:
164 * builder = jpaTester.getJpaBuilder();
165 * dbtester = jpaTester.getDbUtils().createDbTester(new MyTables());
166 * dbutils = jpaTester.getDbUtils();
169 * <a name="db-jpa-services">
170 * <h2>Testing a service that requires a transaction</h2>
173 * <p>Using {@link TransactionProxyFactory} it is possible to create a proxy for a given
174 * service interface to provide the semantics of 'requires new' transaction semantics.
176 * TransactionProxyFactory<Service> factory = new TransactionProxyFactory<Service>(
177 * jpaTester.getJpaBuilder(), Service.class);
178 * Service service = new ServiceImpl(factory.getTransactionScopedEntityManager());
179 * Service proxy = factory.getProxy(service);
180 * proxy.execute(...);
184 * In the above example, the <code>Service</code> POJO requires an {@link EntityManager} in its
185 * constructor and it is passed a transaction scoped entitymanager from the factory. This entitymanager
186 * is in fact a so-called contextual reference.
187 * Next, the proxy is obtained from the factory. Invoking any method on it will make sure a new
188 * transaction is started and a new entity manager is created for the scope of that transaction.
191 * <a name="design-overview">
192 * <h2>design overview</h2>
195 * <h3>Database transaparency</h3>
197 * <h3>Testing using JDBC and DBUnit</h3>
199 * <h3>JPA tester overview</h3>
204 package org.wamblee.support.persistence;
206 import static org.mockito.Mockito.*;
208 import javax.persistence.EntityManager;
210 import org.junit.Before;
211 import org.wamblee.support.persistence.JpaBuilder.JpaUnitOfWork;
212 import org.wamblee.support.persistence.TransactionProxyFactoryTestBase.Service;